OpenAPI pipeline
apps/backend/openapi/openapi.yaml is the durable source of truth for Clay’s HTTP surface. It is regenerated from the live Hono app, linted by Redocly, and consumed by Fern to produce @clay/client.
Purpose
The pipeline keeps the OpenAPI document in lockstep with the implemented routes so that the TypeScript client never drifts from what the Worker actually serves. Treat the YAML as a generated artifact and the Zod schemas insrc/index.ts as the single source.
Pipeline
Hono route schemas
Each route is declared with
createRoute from @hono/zod-openapi. Request and response shapes
are described with Zod, then attached to the route’s responses map.OpenAPI 3.1 document builder
The Hono app exposes the document through
getOpenApiDocument(), which calls
app.getOpenAPI31Document(openApiDocumentConfig). The same document is served live at GET /openapi.json via app.doc31(...) for ad-hoc debugging.Export script
scripts/export-openapi.ts imports getOpenApiDocument(), YAML-stringifies the result with
yaml, and writes it to openapi/openapi.yaml. It uses tsx to execute TypeScript without a
build step.Redocly lint
openapi:check runs redocly lint against the generated YAML to enforce structure, naming, and
operation completeness.Source schemas
The currentsrc/index.ts registers three Zod schemas and two routes. Every schema declared with .openapi("<Name>") becomes a component in the generated YAML.
RootResponseSchema
Inline
z.string() for the GET / plain-text body. Annotated with .openapi({ example: "Hello, world!" }) for inline metadata, but not named — emitted inline under the response schema.HealthResponse
{ status: "ok", service: "clay-backend" } object schema. Registered for reuse across the health surface.ErrorResponse
{ error: string } object schema. Attached to the 404 response on both routes and to the notFound handler.Export step
The export script imports the live Hono app in-process and writes the OpenAPI 3.1.0 document to disk. It depends onyaml for serialization and on tsx for TypeScript execution without a build step.
generate script is an alias for openapi:export, which runs the script above with tsx. The Worker also exposes the same document at runtime through app.doc31("/openapi.json", openApiDocumentConfig), which is useful for live debugging without rebuilding.
Validation step
Redocly CLI lints the generated YAML against its built-in OpenAPI ruleset. Run it after every schema or route change so the downstream Fern generator never receives a malformed contract.Downstream consumer
Fern readsapps/backend/openapi/openapi.yaml and generates the typed @clay/client package. The client is what apps/mobile and any future Clay consumer calls — never the raw HTTP surface.
OpenAPI YAML
apps/backend/openapi/openapi.yaml — generated contract.Fern generator
fernapi/fern-typescript-sdk@3.72.5 with fetchSupport: native, wired by the ts-sdk group in
packages/client/fern/generators.yml.@clay/client
packages/client/ — the typed TypeScript package shipped to apps.Regenerate commands
| Task | Command |
|---|---|
| Regenerate the OpenAPI YAML | pnpm --filter @clay/backend generate |
| Lint the generated YAML | pnpm --filter @clay/backend openapi:check |
| Typecheck the backend | pnpm --filter @clay/backend typecheck |
| Build the backend (typecheck) | pnpm --filter @clay/backend build |
| Regenerate the SDK | pnpm --filter @clay/client generate |
| Validate Fern config | pnpm --filter @clay/client check |
| Wipe the generated artifact | pnpm --filter @clay/backend clean |
Drift rules
Schemas live next to handlers
Define route schemas with Zod in the same file as the handler that serves the route. Avoid a separate
schemas/ folder until the surface grows.Reuse shared shapes
Register cross-route shapes with
.openapi("<Name>") so they appear under components.schemas. Inline Zod chains stay inline for single-use shapes.Lint after every change
Run
openapi:check after touching a route, a schema, or the export script. The lint is the first gate before Fern regenerates @clay/client.Never hand-edit the YAML
If the YAML disagrees with the code, the code is the source of truth. Regenerate rather than patch.
Acceptance criteria
The YAML regenerates cleanly with
pnpm --filter @clay/backend generate.Redocly reports zero errors with
pnpm --filter @clay/backend openapi:check.Every registered
.openapi("<Name>") schema appears under components.schemas.Every
createRoute operation has a matching entry under paths.Fern can consume the YAML and produce
@clay/client without manual fixes.The runtime
/openapi.json document and the exported YAML agree on every operation.Related
Backend overview
The Hono app and Cloudflare Worker this pipeline describes.
API reference
The implemented routes and Zod schemas behind the contract.
Environment and bindings
The Wrangler bindings and runtime secrets the Worker exposes.
@clay/client package
The Fern-generated TypeScript SDK consumed by apps.
Architecture
Where this pipeline sits inside the Turborepo surface layout.

