| name | api-doc |
| description | Generate or refresh the OpenAPI 3.1 spec for @butler/api from the live Hono routes and the @butler/shared Zod schemas, so the API has a single source of truth. Use when an endpoint or its request/response schema changes, or when you need an up-to-date API spec. |
| disable-model-invocation | true |
API Doc
Generate packages/api/openapi.json — an OpenAPI 3.1 spec for the Butler API — from a single source of truth, so the document cannot silently drift from the code:
- Route list comes from the live Hono app (
app.routes). Every registered method + path is enumerated at runtime, so the spec always matches what is actually mounted in packages/api/src/app.ts.
- Request/response shapes come from the same
@butler/shared Zod schemas the routes validate with via @hono/zod-validator. The generator converts them with Zod 4's native z.toJSONSchema. Because the validator and the generator reference the same schema object, the documented shape and the enforced shape are guaranteed identical.
This is the deliberately small first slice. Broadly migrating every route to @hono/zod-openapi (which would let each route carry its own response schema and OpenAPI metadata inline) is a larger architectural change and deserves its own ADR/issue — see issue #143.
Run it
pnpm --filter @butler/api openapi
This builds the package (tsc --build) and runs packages/api/scripts/generate-openapi.mjs, which writes packages/api/openapi.json. The script validates structurally before writing (OpenAPI version is 3.1.0, at least one path, every $ref resolves to a defined component, the document round-trips as JSON) and exits non-zero on any failure, so a broken spec is never written. Output is deterministic — re-running with no code changes produces a byte-identical file.
packages/api/openapi.json is a generated artifact (git-ignored, like dist/): regenerate it on demand rather than hand-editing or committing it. Because it is reproducible, anyone can recreate the current spec from source with the one command above.
Add or correct an endpoint's body/response
Every registered route is always listed in the spec (so coverage is visible at a glance), but only routes with an entry in SCHEMA_MAP carry a typed request/response body. To describe a new or changed endpoint:
- Open
packages/api/scripts/generate-openapi.mjs.
- Add (or edit) one line in
SCHEMA_MAP, keyed by "METHOD path" exactly as Hono reports it (e.g. "POST /api/clubs/:clubId/bookings"), pointing at the @butler/shared schema the route uses:
"POST /api/clubs/:clubId/bookings": { body: ["CreateBookingRequest", CreateBookingRequestSchema] },
"GET /api/me": { response: ["MeResponse", MeResponseSchema] },
Use the schema the route's zValidator("json", …) already references — never a hand-copied duplicate. That is what keeps the doc in sync.
- Re-run
pnpm --filter @butler/api openapi and confirm the new endpoint shows the expected requestBody / response schema.
SCHEMA_MAP is intentionally the only place a route is bound to a schema. Keep it that way: a new GET route appears in the spec automatically; giving it a typed response is a one-line addition.
When to run
- A route is added, removed, or renamed in
packages/api/src/routes/.
- A request/response Zod schema in
@butler/shared changes shape.
- Before publishing or reviewing the API surface, to produce a current spec to hand to consumers or inspect.