--- phase: 26-discovery-landing-page plan: "02" subsystem: server/client tags: [discovery, http-routes, react-query, rate-limiting] dependency_graph: requires: [26-01] provides: [discovery-http-endpoints, discovery-react-hooks] affects: [server/index.ts, client/hooks] tech_stack: added: [] patterns: [hono-route-handler, react-query-hook, cursor-pagination] key_files: created: - src/server/routes/discovery.ts - src/client/hooks/useDiscovery.ts - tests/routes/discovery.test.ts modified: - src/server/index.ts decisions: - "No cursor pagination needed for getTrendingCategories — bounded small list, simple limit is sufficient (carried from plan 01)" - "discoveryRoutes registered with browseTier rate limiting (120 req/min) for all GET discovery endpoints" - "Auth skip added for /api/discovery/* GET — public access without authentication" metrics: duration: "~8 minutes" completed: "2026-04-10" tasks_completed: 2 files_created: 3 files_modified: 1 --- # Phase 26 Plan 02: Discovery HTTP Routes and React Query Hooks Summary **One-liner:** Three public GET endpoints at /api/discovery/{setups,items,categories} with browseTier rate limiting, wired to discovery service from plan 01, plus matching React Query hooks with typed interfaces. ## What Was Built ### Task 1: Discovery routes, server registration, and route tests Created `src/server/routes/discovery.ts` with three Hono GET handlers following the exact pattern of `global-items.ts`: - `GET /setups` — calls `getPopularSetups(db, limit, cursor)`, default limit 6, max 50 - `GET /items` — calls `getRecentGlobalItems(db, limit, cursor)`, default limit 8, max 50 - `GET /categories` — calls `getTrendingCategories(db, limit)`, default limit 12, max 50 Updated `src/server/index.ts`: - Added `discoveryRoutes` import - Added `browseTier` rate limiting for `GET /api/discovery/*` - Added auth skip: `if (c.req.path.startsWith("/api/discovery") && c.req.method === "GET") return next()` - Registered `app.route("/api/discovery", discoveryRoutes)` Created `tests/routes/discovery.test.ts` with 10 tests covering: - Response shape validation for all three endpoints - Empty state handling - Limit param enforcement - Cursor-based pagination for items endpoint - Public-only filter for setups ### Task 2: Client-side React Query hooks Created `src/client/hooks/useDiscovery.ts` with three named hook exports: - `useDiscoverySetups(limit = 6)` — queryKey `["discovery", "setups", limit]`, staleTime 2min - `useDiscoveryItems(limit = 8)` — queryKey `["discovery", "items", limit]`, staleTime 2min - `useDiscoveryCategories(limit = 12)` — queryKey `["discovery", "categories", limit]`, staleTime 5min Exported interfaces: `DiscoverySetup`, `DiscoveryCategory`. ## Verification - `bun test tests/routes/discovery.test.ts` — 10 pass, 0 fail - `bun run build` — clean build, no TypeScript errors - Full test suite: 285 pass, 15 pre-existing failures in unrelated modules (storage.service.ts export issue in setups/items/profiles/threads routes tests) ## Deviations from Plan ### Auto-fixed Issues **1. [Rule 1 - Bug] Fixed cursor pagination test with simultaneous timestamps** - **Found during:** Task 1 test writing - **Issue:** Two `globalItems` inserted in quick succession in PGlite got the same `defaultNow()` timestamp, making pagination impossible to test - **Fix:** Inserted items with explicit `createdAt` values (2024-01-01 and 2024-06-01) to ensure distinct timestamps for pagination test - **Files modified:** tests/routes/discovery.test.ts - **Commit:** 0323e0c ## Known Stubs None — all endpoints return live database data from the discovery service. ## Self-Check: PASSED Files exist: - FOUND: src/server/routes/discovery.ts - FOUND: src/client/hooks/useDiscovery.ts - FOUND: tests/routes/discovery.test.ts Commits exist: - 0323e0c — feat(26-02): discovery HTTP routes, server registration, and route tests - 747a1c3 — feat(26-02): React Query hooks for discovery data