API Architecture
Fastify 5 + TypeBox routes generate OpenAPI; @hey-api/openapi-ts generates @repo/core. React Query hooks in @repo/react are handwritten.
Fastify routes and TypeBox schemas are the source of truth for validation and for the OpenAPI spec. @hey-api/openapi-ts generates the TypeScript client in @repo/core. @repo/react wraps that client with handwritten TanStack Query 5 hooks — Hey API does not generate them.
Flow: TypeBox → OpenAPI → @repo/core (client + types) → handwritten @repo/react hooks. Commands: OpenAPI Generation.
Stack
- Runtime: Node.js 24.x (LTS Krypton)
- Framework: Fastify 5, TypeBox, ESM
- Database: PostgreSQL via
DATABASE_URL(PGLite whenPGLITE=true) - ORM: Drizzle
- Spec: OpenAPI 3 at
apps/api/openapi/openapi.json, UI at/reference
How it works
- Author a route with TypeBox in
apps/api/src/routes/(one file per endpoint). - Generate OpenAPI from those routes.
- Generate
@repo/core. Add or update hooks in@repo/reactby hand when the UI needs them.
Runtime: request → Fastify validation → handler → Drizzle → JSON.
import { Type } from '@sinclair/typebox'
import { dbHealth } from '../db/probe.js'
fastify.get('/health', {
schema: {
response: {
200: Type.Object({ ok: Type.Boolean(), dbReady: Type.Boolean() }),
503: Type.Object({ ok: Type.Boolean(), dbReady: Type.Boolean() }),
},
},
}, async (_request, reply) => {
const dbReady = await dbHealth.probe()
if (!dbReady) return reply.code(503).send({ ok: false, dbReady: false })
return reply.code(200).send({ ok: true, dbReady: true })
})GET /health is readiness: 200 when SELECT 1 succeeds, 503 when the store cannot answer. It does not probe Resend, AI, or IdPs.
createClient from @repo/core has three auth modes (see Authentication): no-auth, JWT (getAuthToken / refresh), API key.
import { createClient } from '@repo/core'
import { useHealthCheck } from '@repo/react'
const client = createClient({ baseUrl: process.env.NEXT_PUBLIC_API_URL! })
const { data } = useHealthCheck()Other languages can use any OpenAPI generator against the same spec. Local DB: pnpm reset (Supabase reset + Drizzle migrate + seed). See ADR 008.
Security
Security headers, CORS via ALLOWED_ORIGINS, per-IP rate limiting, TypeBox validation, trustProxy behind a reverse proxy. Details: Security.