Skip to content

feat(docs): replace @nestjs/swagger with a zod-native OpenAPI generator - #79

Merged
joshunrau merged 3 commits into
mainfrom
docs-fix
Oct 1, 2026
Merged

joshunrau merged 3 commits into
mainfrom
docs-fix

Conversation

@joshunrau

Copy link
Copy Markdown
Collaborator

Why

API docs were broken for any app that types controller parameters as Zod schemas, the pattern where a type and a schema share a name, e.g. @Body() data: $CreateCatData. SWC's decorator metadata emits the schema instance itself as the parameter type, and @nestjs/swagger assumes every parameter type is a class:

  • Zod request bodies produced $ref: "#/components/schemas/", which made Redoc fail with "Invalid reference token". This is why /docs in the example app was broken.
  • Unnamed Zod @Query()/@Param()/@Headers(), such as Clinivance's @SearchParams(), crashed the app at startup with Cannot read properties of undefined (reading 'constructor').

What changed

  • Route order fix (fix(app)). configureDocs now runs after enableVersioning, so documented paths include the version prefix (/v1/...).
  • Zod-native OpenAPI generator (feat(docs), breaking). It replaces @nestjs/swagger:
    • Routes are resolved the same way the Nest router does it, using Nest's own PathsExplorer and RoutePathFactory. That covers versioning, the global prefix and RouterModule paths.
    • A Zod @Body() becomes the request body. Unnamed Zod query, path and header parameters become one parameter per property. Named parameters are documented from their Zod schema or primitive type. Any other type is documented as unknown.
    • Schemas come from z.toJSONSchema, so enums, nullables, defaults and descriptions are kept. Dates are documented as date-time strings. Schemas with .meta({ id }) become shared components, and recursive schemas get unique names.
    • The output is OpenAPI 3.1. A route that can't be documented logs a warning instead of crashing startup.
    • Redoc is pinned to v2.5.4, and the dead styles.css link is gone.
    • @ValidationSchema is deprecated. Classes using it are documented as unknown.
  • ApiOperation decorator (feat(docs)). It sets an operation's summary, description and deprecated flag. The options mirror @nestjs/swagger's, so existing usages only need a new import.

Breaking changes

  • @nestjs/swagger decorators (@ApiProperty, @ApiTags, swagger's @ApiOperation) no longer affect the docs.
  • @ValidationSchema is deprecated. It still validates, but its classes are documented as unknown.

Not included

  • The @nestjs/swagger dependency is still in package.json. No code imports it any more. Removing it should be a follow-up commit, run outside the Claude Code sandbox: inside it, pnpm can't read ~/.npmrc and rewrites the whole lockfile.
  • Response schemas aren't documented, only status codes. A handler's runtime return type is just Promise, so they would need an explicit decorator. That will be a separate issue.

Testing

  • Unit tests cover every case above, and coverage stays at 100%.
  • A drift test boots an app with versioning, a global prefix and RouterModule, records every route Fastify registers, and asserts the spec lists exactly those method and path pairs. This guards the Nest internals the generator relies on.
  • The example app's /docs page renders in Redoc with no errors.

🤖 Generated with Claude Code

joshunrau and others added 3 commits September 28, 2026 11:53
The OpenAPI document reads the app's versioning config when it builds its
paths, so generating it before enableVersioning left the version prefix out
of every documented path (e.g. /things instead of /v1/things).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Swagger's class-based reflection cannot handle controller parameters typed
as Zod schemas: bodies produced an empty $ref that crashed Redoc, and
unnamed query/path/header parameters crashed the app at startup.

The new generator resolves routes the same way the Nest router does and
documents parameters from their Zod schemas via z.toJSONSchema, emitting an
OpenAPI 3.1 document. Redoc is pinned to v2.5.4.

BREAKING CHANGE: API docs are generated from the Zod schemas that controller
parameters are typed with. @nestjs/swagger decorators are no longer read, and
@ValidationSchema is deprecated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lets route handlers set the summary, description, and deprecation status of
their operation in the generated OpenAPI document. The options mirror the
ones from @nestjs/swagger, so existing usages only need a new import.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Sep 30, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (724620d) to head (7661d36).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main       #79   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           62        64    +2     
  Lines          739       838   +99     
  Branches       125       147   +22     
=========================================
+ Hits           739       838   +99     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@joshunrau
joshunrau merged commit ce20531 into main Oct 1, 2026
3 checks passed
@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown

🎉 This PR is included in version 9.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant