Skip to content

Feature: Stream Search And Tag Filtering #532

Description

@Xhristin3

Context

GET /streams filters by status, visibility, and ownerOnly (api/src/streams/dto/list-streams.query.dto.ts, ListStreamsQueryDto), but a user cannot find a stream by name or by tag. The platform's discovery story is "list everything, then page through it": with the tags feature live (GET /tags, per-stream tag chips), the natural next query — "streams tagged live" or "streams matching football" — requires the client to fetch pages of every stream and filter locally. That is untenable at any real scale (pagination caps at 100 per page, PaginationQueryDto), and the SDK's paginateAll helper (xstreamroll-sdk/src/pagination.ts) would walk the entire table to answer a single search.

The maintenance trap a naive fix would create is filtering in the app layer (streams-list-live.tsx post-processing fetched pages), which breaks pagination totals and duplicates logic that belongs in one SQL predicate.

Goal

Add server-side search and tag filtering to GET /streams: a q parameter matching stream name/description, and a tag parameter (slug or id) restricting results to streams with that tag — applied inside the existing visibility-aware query so totals and hasMore stay correct.

Scope

1. Query parameters

  • q (case-insensitive substring on name, and optionally description) and tag (tag slug or id, resolved via TagsService/TagsDbRepository.findBySlug). Extend ListStreamsQueryDto with validation (length caps, format), keeping the existing visibility/status/ownerOnly filters orthogonal.

2. Repository predicate

  • Extend StreamsDbRepository.listPaginated (api/src/streams/repository/streams-db.repository.ts) with the new predicates in the shared WHERE fragment (the COUNT and SELECT already share it, so totals stay correct by construction). The tag filter joins stream_tags/tags; the q filter uses ILIKE with a bounded pattern (escape %/_ so user input cannot become a wildcard). Keep the in-memory StreamsRepository behaviorally equivalent for unit tests.

3. SDK and app surface

  • Add q/tag params to the SDK's stream-listing types/helpers (xstreamroll-sdk/src/types.ts, client.ts if it grows a list method) and to app/lib/api/streams.ts listStreams, so the dashboard search box can pass them through. No UI is required by this issue beyond plumbing the params.

Downstream impact

  • api/src/streams/dto/list-streams.query.dto.ts, api/src/streams/repository/streams-db.repository.ts, api/src/streams/repository/streams.repository.ts (in-memory), api/src/streams/streams.controller.ts (Swagger docs).
  • app/lib/api/streams.ts, app/hooks/useStreams.ts (query keys must include the new params or cache collisions occur).
  • xstreamroll-sdk/src/types.ts (pagination params), README/docs for the list endpoint.
  • Contracts: extend tests/contracts/src/streams.contract.ts with a search query case so provider/consumer suites pin the new params.

Acceptance criteria

Contract

  • GET /streams?q=foo returns only streams whose name (and description, if chosen) contains foo (case-insensitive), respecting visibility and ownerOnly.
  • GET /streams?tag=<slug> returns only streams carrying that tag, respecting visibility; an unknown tag returns an empty page (not an error) or 404 per the documented choice.
  • total and hasMore in the response reflect the filtered set, not the unfiltered table.

Service

  • q input with %/_ is treated literally (escaped), not as a SQL wildcard.
  • The tag filter reuses the existing tag lookup (TagsDbRepository.findBySlug) and does not duplicate slugification logic.

Tests

  • Repository-level tests cover: q matching name and description, case-insensitivity, q + status/visibility combinations, tag filtering, and the escaped-wildcard case.
  • Controller/DTO tests cover validation of q length and tag format.
  • Contract test for a filtered query passes in both provider and consumer suites.

Documentation

  • Swagger documents q and tag with examples; SDK/README list-endpoint docs mention them.

Out of scope

Full-text search ranking, fuzzy matching, and the dashboard search UI itself.

Getting started

Real files in scope: api/src/streams/dto/list-streams.query.dto.ts, api/src/streams/repository/streams-db.repository.ts (listPaginated), api/src/streams/repository/streams.repository.ts, api/src/streams/streams.controller.ts, app/lib/api/streams.ts, app/hooks/useStreams.ts, xstreamroll-sdk/src/types.ts, tests/contracts/src/streams.contract.ts.

Verify with:

cd api && npm run typecheck && npm test
cd ../app && npm run typecheck && npm test
cd ../xstreamroll-sdk && npm run typecheck && npm test

Good first files to read: api/src/streams/repository/streams-db.repository.ts (listPaginated WHERE fragment), api/src/streams/dto/list-streams.query.dto.ts, api/src/tags/repository/tags-db.repository.ts (findBySlug).

Metadata

Metadata

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardThird CampaignCampaign: Third CampaignapiREST API design and endpointsenhancementNew feature or requestfeature

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions