Overview
Every API route in folksbase follows the same pattern: a Hono route handler with OpenAPI documentation viahono-openapi, request validation via Zod, and strict adherence to the layered architecture (Routes → Services → Repositories → Drizzle).
This guide walks through adding a new route from scratch.
The Layered Architecture
Before writing any route code, understand the layers:| Layer | Responsibility | Location |
|---|---|---|
| Route | HTTP concerns only — parse request, call service, return response | src/routes/*.ts |
| Service | Business logic, orchestration, validation | src/services/*.ts |
| Repository | SQL queries only, no business logic | src/repositories/*.ts |
- Routes never import from
@folksbase/db - Services never construct HTTP responses
- Repositories never contain business logic
Step 1: Define Your Zod Schemas
Start with the request and response schemas. If they’re reusable, add them toapps/api/src/lib/openapi-schemas.ts. Otherwise, define them in the route file.
Step 2: Create the Route File
Create a new file inapps/api/src/routes/. Import the OpenAPI helpers and auth middleware:
Step 3: Add Route Handlers with OpenAPI Docs
Every route must usedescribeRoute() for response documentation and zValidator() for request validation:
Key Patterns
- Path params use
zValidator("param", schema)— always enforce UUID format: - Query params use
zValidator("query", schema) - JSON body uses
zValidator("json", schema) - Access validated data via
c.req.valid("query"),c.req.valid("json"), orc.req.valid("param")
Step 4: Register the Route
Add your route toapps/api/src/index.ts:
Step 5: Error Handling
Don’t add manual try/catch blocks in route handlers. Let errors propagate to the globalerrorHandler middleware, which handles ZodError and auth errors automatically and returns consistent { code, message } responses.
The only exception is when you need to return a specific 404:
Step 6: Verify
After adding your route:- Start the dev server:
pnpm --filter @folksbase/api dev - Open http://localhost:3001/api/docs — your new route should appear in the Scalar UI
- Check the OpenAPI spec at http://localhost:3001/api/openapi.json
- Write unit tests in
src/routes/__tests__/following the existing patterns
Checklist
Before submitting your PR, verify:- Route uses
describeRoute()with tags, summary, and response schemas - Request validation uses
zValidator()(not manual.parse()) - Route only calls services — no direct DB imports
- Path params enforce UUID format where applicable
- No manual try/catch — errors propagate to global handler
- Unit tests use valid UUIDs for path parameters
- Route is registered in
src/index.ts