# Codebase Improvements Design **Date:** 2026-04--03 **Scope:** General code quality, error handling, resilience, and maintainability improvements ## 1. Server Hardening ### 1a. Explicit DB Context Middleware **File:** `src/server/index.ts` Add middleware that explicitly sets `c.set("db", prodDb)` for all API routes. Currently routes call `c.get("db")` but nothing sets it in production — services silently fall back to `prodDb` via default parameters. This makes production behavior match the test pattern. ```ts import { db as prodDb } from "../db/index.ts"; app.use("/api/*", async (c, next) => { c.set("db", prodDb); return next(); }); ``` Place this **before** the auth middleware so `db` is available when auth checks run. ### 1b. Route Parameter Validation **New file:** `src/server/lib/params.ts` Create a helper that validates numeric route params: ```ts export function parseId(raw: string): number | null { const id = Number(raw); if (!Number.isInteger(id) || id <= 0) return null; return id; } ``` Update all route files (`items.ts`, `threads.ts`, `categories.ts`, `setups.ts`) to replace `Number(c.req.param("id"))` with `parseId()`, returning 400 for invalid IDs. ### 1c. Centralized Error Handling **File:** `src/server/index.ts` Add Hono's `onError` handler: ```ts app.onError((err, c) => { console.error(`[${c.req.method}] ${c.req.path}:`, err); const status = err instanceof HTTPException ? err.status : 500; const message = process.env.NODE_ENV === "production" ? "Internal server error" : err.message; return c.json({ error: message }, status); }); ``` ### 1d. Auth Comment Fix **File:** `src/server/index.ts` Change comment from: ``` // Auth middleware for write operations (POST/PUT/DELETE) on non-auth routes ``` To: ``` // Auth middleware for write operations (POST/PUT/PATCH/DELETE) on non-auth routes ``` ### 1e. Rate Limiting on Auth Endpoints **New file:** `src/server/middleware/rateLimit.ts` In-memory rate limiter using a `Map`: - Tracks by IP (`c.req.header("x-forwarded-for") || "unknown"`) - 5 attempts per 15-minute window - Returns 429 with `{ error: "Too many attempts. Try again later." }` and `Retry-After` header - Stale entries cleaned on each check - Applied to `POST /api/auth/login` and `POST /api/auth/setup` ## 2. Client Resilience ### Error Boundary **File:** `src/client/routes/__root.tsx` Add `errorComponent` to the root route definition: ```ts export const Route = createRootRoute({ component: RootLayout, errorComponent: RootErrorBoundary, }); ``` `RootErrorBoundary` renders a centered error message with: - "Something went wrong" heading - Error message in dev mode - "Try again" button that calls `router.invalidate()` + `reset()` Uses TanStack Router's `ErrorComponentProps` which provides `error` and `reset`. ## 3. Client Refactor ### Split collection/index.tsx Extract the three tab-level functions into separate component files: | Source function | New file | Approx lines | |----------------|----------|-------------| | `CollectionView()` | `src/client/components/CollectionView.tsx` | ~260 | | `PlanningView()` | `src/client/components/PlanningView.tsx` | ~190 | | `SetupsView()` | `src/client/components/SetupsView.tsx` | ~110 | `collection/index.tsx` keeps: - Route definition with `searchSchema` and `validateSearch` - `CollectionPage` function (tab switcher + AnimatePresence) - `TAB_ORDER` and `slideVariants` constants - Imports from the three new component files Each extracted component is a named export, self-contained with its own hooks and local state. ## 4. Docs Cleanup ### PROJECT.md **File:** `.planning/PROJECT.md` Update Constraints section line: ``` - **Scope**: No auth, single user for v1 ``` To: ``` - **Scope**: Single user with cookie/API key auth ``` ## Commit Strategy Group into 3-4 commits by area: 1. **Server hardening**: DB middleware, param validation, error handler, rate limiter, comment fix 2. **Client resilience + refactor**: Error boundary, split collection route 3. **Docs cleanup**: PROJECT.md update ## Testing - All 183 existing tests must continue to pass - Rate limiter: manual verification (no automated test needed for in-memory rate limiting in a single-user app) - Error boundary: manual verification by triggering a render error - Param validation: existing route tests cover happy paths; invalid IDs are a new edge case but won't break existing tests