OpenAPI Generation
TypeBox routes → OpenAPI → @repo/core. React Query hooks in @repo/react are handwritten.
TypeBox on Fastify routes is the source of truth. Generate OpenAPI, then the @repo/core client. Add TanStack Query hooks in @repo/react by hand when the UI needs them. There is no packages/react/src/gen/ and no Hey API React plugin in this repo.
Commands
pnpm generate # openapi.json + packages/core/src/gen (when routes change)
pnpm --filter @repo/api generate:openapi # apps/api/openapi/openapi.json only
pnpm --filter @repo/core generate # packages/core/src/gen/@repo/web, @repo/docu, and package CI builds use committed openapi.json and src/gen/. Regenerate after route changes; api-e2e runs pnpm generate && git diff --exit-code on API PRs. The API Vercel build still runs generate:openapi inside @repo/api build.
Spec also served at /reference/openapi.json; Scalar UI at /reference. Config: packages/core/openapi-ts.config.ts.
Vercel / production builds
generate:openapi boots the Fastify app to scan routes. When env vars are missing (e.g. @repo/web on Vercel with NODE_ENV=production), the script stubs production-only values before import: ALLOWED_ORIGINS, APP_NAME, secrets, and PGLITE. Runtime API CORS rules in apps/api/src/lib/env.ts are unchanged—stubs apply only to codegen.
New endpoint
- Add the route + TypeBox schema in
apps/api/src/routes/(one file per endpoint). pnpm --filter @repo/api generate:openapipnpm --filter @repo/core generate- If the UI needs a hook, add it under
packages/react/src/hooks/and export frompackages/react/src/index.ts.
import { createClient } from '@repo/core'
import { useUser } from '@repo/react'
const client = createClient({
baseUrl: process.env.NEXT_PUBLIC_API_URL!,
getAuthToken: async () => session?.token,
getRefreshToken: async () => session?.refreshToken,
onTokensRefreshed: async ({ token, refreshToken }) => { /* persist */ },
})
const user = await client.auth.session.user()
const { data } = useUser()See API Architecture and Package Conventions.