A mobile-first web app (brand name: BingeLoop) where users log TV shows and movies, rate them 1–5 stars, leave a comment, and tag a category (Drama, Comedy, Thriller, etc.).
pnpm --filter @workspace/api-server run dev— run the API server (port 5000)pnpm run typecheck— full typecheck across all packagespnpm run build— typecheck + build all packagespnpm --filter @workspace/api-spec run codegen— regenerate API hooks and Zod schemas from the OpenAPI specpnpm --filter @workspace/db run push— push DB schema changes (dev only)- Required env:
DATABASE_URL— Postgres connection string
- pnpm workspaces, Node.js 24, TypeScript 5.9
- API: Express 5
- DB: PostgreSQL + Drizzle ORM
- Validation: Zod (
zod/v4),drizzle-zod - API codegen: Orval (from OpenAPI spec)
- Build: esbuild (CJS bundle)
- Frontend:
artifacts/watchlist/(React + Vite, served at/) - API contract (source of truth):
lib/api-spec/openapi.yaml - DB schema:
lib/db/src/schema/ - API routes:
artifacts/api-server/src/routes/; shared auth inmiddlewares/requireAuth.ts; membership helpers inlib/groups.ts - Generated hooks:
@workspace/api-client-react; generated Zod:@workspace/api-zod
Users create and name their own groups, invite others by link, belong to multiple groups, and switch the active group (new accounts with no groups land on an Onboarding screen to create their first). The library, hero stats, and "also engaged by" attribution are all scoped to the active group's members. Within that scope users browse their watch library with category/media-type filters and sorting, see summary stats (total logged, average rating, movie/TV split, category breakdown), and add/edit/delete entries — each with a title, movie/TV type, 1–5 star rating, category, and optional comment. Entries, watchlist saves, and audience picks remain personal to each user; you only see another member's data when you share a group with them.
Detailed notes are split into focused docs under docs/:
docs/architecture.md— core decisions: entries/categories/stats/auth, groups & membership (incl. soft-removal), privacy/data-scoping, and the non-membergroupId403 contract.docs/features.md— per-feature detail: watchlist, who-should-watch poll, spicy flag, comments, activity feed, new-activity badge, Top Four, lists, invites, TMDB search, emoji reactions, weekly digest.docs/design.md— the light "cinematic editorial" theme, fonts, and the navy.poster-cardtoken re-scoping.docs/roadmap.md— parked ideas not yet built (incl. deferred email/push digest delivery).
Populate as you build — explicit user instructions worth remembering across sessions.
- API auth depends on router MOUNT ORDER.
entriesRoutercallsrouter.use(requireAuth)with no path, and every sub-router inroutes/index.tsis mounted viarouter.use(...)with no path prefix — so that global auth runs for EVERY request that reachesentriesRouterand 401s anything still unhandled. Any PUBLIC route (e.g.GET /invites/:token) MUST be mounted beforeentriesRouter;healthRouterandinvitesRouterare intentionally mounted first. Do not reorder routers without preserving this.
- See the
pnpm-workspaceskill for workspace structure, TypeScript setup, and package details