Skip to content

Generating the OpenAPI Spec

Lombok’s HTTP API is described by an OpenAPI 3.1 document that is generated from the NestJS backend’s controllers and nestjs-zod DTOs. The generated document and the TypeScript types derived from it are committed to the repository, so any change to the API surface needs to be followed by regenerating these artifacts and committing the result.

This page covers the generation workflow. It assumes you already have a working dev environment. For setup and the broader local development loop, see Local Development with dx.

Running the generator produces three artifacts, all under version control:

FileProduced byPurpose
packages/api/src/nestjs-metadata.tsgenerate:metadataNestJS Swagger plugin metadata
packages/api/src/openapi.jsongenerate:metadataThe OpenAPI 3.1 document
packages/types/src/api-paths.d.tsgenerate-openapi-spec.shTypeScript paths types generated from openapi.json

The openapi.json document is consumed by the @lombokapp/types package, which exposes the paths type and derives the API DTO types used by the UI and other packages. Regenerating keeps these types in sync with the backend.

The simplest path runs the full chain — metadata, spec, and the derived TypeScript types:

Terminal window
./dx generate openapi

This requires the dev container to be running. Under the hood it runs bun --cwd packages/api generate:openapi, which is:

generate:metadata -> ./cmd/generate-openapi-spec.sh

You can run the same thing from the repo root without dx:

Terminal window
bun run generate:openapi

If you only want to refresh nestjs-metadata.ts and openapi.json without rebuilding the derived types:

Terminal window
./dx generate metadata

This runs inside the dev container. It bootstraps the Nest application in preview mode, walks the TypeScript sources with the @nestjs/swagger ReadonlyVisitor, and writes the two files.

The generator lives in packages/api/script/generate-metadata.ts. At a high level it:

  1. Type-checks the API sources, then runs the @nestjs/swagger plugin’s ReadonlyVisitor to collect type metadata and write src/nestjs-metadata.ts.
  2. Boots CoreModule in preview mode and builds the document with DocumentBuilder.
  3. Integrates nestjs-zod via cleanupOpenApiDoc so Zod-inferred DTOs land in the spec.
  4. Runs a series of normalization and compression passes over the document.
  5. Writes the result to packages/api/src/openapi.json.

The shell step (packages/api/cmd/generate-openapi-spec.sh) then runs openapi-typescript against openapi.json to produce packages/types/src/api-paths.d.ts, and rebuilds the @lombokapp/types package.

These passes are why the committed spec does not look like a raw NestJS dump — the schema names and structure are deliberately normalized for cleaner downstream type generation.

Regenerate whenever you change anything that affects the API contract, for example:

  • adding, removing, or renaming a controller route
  • changing a request or response DTO
  • changing query or path parameters
  • changing status codes or response shapes

Commit the regenerated nestjs-metadata.ts, openapi.json, and api-paths.d.ts alongside your code change so the spec and derived types stay consistent with the backend.

[!NOTE] The repository does not currently appear to enforce spec freshness in CI, so it is on the contributor to regenerate and commit these artifacts.

  • ./dx generate ... fails because nothing is running — these commands expect the dev container to be up. Run ./dx up first.
  • Type errors during generationgenerate:metadata type-checks the API sources before emitting. Fix the reported TypeScript errors, then rerun.
  • A DTO is missing or shows an empty schema — confirm the DTO is wired through nestjs-zod and reachable from CoreModule.