Skip to content

feat(zod-openapi): add doc32() and getOpenAPI32Document() for OpenAPI 3.2 - #2136

Open
mqmalagris wants to merge 1 commit into
honojs:mainfrom
mqmalagris:feat/zod-openapi-openapi-32
Open

mqmalagris wants to merge 1 commit into
honojs:mainfrom
mqmalagris:feat/zod-openapi-openapi-32

Conversation

@mqmalagris

@mqmalagris mqmalagris commented Sep 8, 2026 •

Copy link
Copy Markdown

The author should do the following, if applicable

  • Add tests
  • Run tests
  • pnpm changeset at the top of this repo and push the changeset
  • Follow the contribution guide

Closes #2039

Problem

@hono/zod-openapi can emit OpenAPI 3.0 and 3.1 documents, but not 3.2. That leaves the 3.2 tag hierarchy out of reach — parent, summary and kind on the Tag Object — which is what replaces the x-tagGroups extension for sidebar grouping in docs renderers.

Root cause

Nothing was blocking it any more, which is the useful part of this one. The issue was filed when the package still pinned @asteasolutions/zod-to-openapi@^8.5.0, and asked for an engine bump. That bump has since landed, so both pieces are already installed and simply unused:

  • @asteasolutions/zod-to-openapi@^9.1.0 is a current dependency and exports OpenApiGeneratorV32 alongside OpenApiGeneratorV3 / OpenApiGeneratorV31.
  • openapi3-ts@4.6.0 already ships an oas32 entrypoint next to oas30 / oas31.

Only the wiring in packages/zod-openapi/src/index.ts was missing.

Fix

Mirrors the existing 3.0 and 3.1 pairs exactly, no new patterns:

  • getOpenAPI32Document(objectConfig, generatorConfig?), backed by OpenApiGeneratorV32 and returning OpenAPIObject from openapi3-ts/oas32. Applies addBasePathToDocument the same way its siblings do.
  • doc32(path, configureObject, configureGenerator?), serving that document on a route.
  • README section documenting both, including a hierarchical-tag example.

Base path handling under .route() and the optional generator options behave as they do for 3.1.

Test

5 tests in a new OpenAPI 3.2 block in packages/zod-openapi/src/index.test.ts:

  1. getOpenAPI32Document() emits openapi: '3.2.0' and the registered paths.
  2. doc32() serves that document over HTTP with status 200.
  3. The 3.2 hierarchical tag fields (parent / summary / kind) survive into the document.
  4. The base path is applied, so a router mounted at /api yields /api/books.
  5. Generator options pass through doc32() (checked via unionPreferredType: 'anyOf', matching the existing doc31 generator-options test).

To run: pnpm test in packages/zod-openapi. Result: vitest run 150/150 passing with Type Errors no errors, tsc -b tsconfig.json clean, eslint back to its baseline of 0 errors. All 5 fail against the unpatched index.ts with getOpenAPI32Document is not a function / doc32 is not a function.

One decision to flag

doc32 mirrors doc31 line for line, which means it carries the same two any boundaries: catch (e: any) and the as any on the returned builder. Those add one @typescript-eslint/no-unsafe-argument and one no-unsafe-return, which pushed src/index.ts past its counts in eslint-suppressions.json — at which point lint reports every occurrence of those two rules in the file, not just the new ones.

I bumped the two counts by one each via eslint --suppress-rule (14 → 15 and 5 → 6, a 3-line diff) rather than write doc32 differently from its siblings. Since autofix.yml only runs --prune-suppressions and never adds, updating the counts in the PR looked like the intended path. Say the word if you would rather doc32 avoid the any boundaries and leave eslint-suppressions.json untouched — happy to switch it either way.

… 3.2

The engine bump already landed: the package pins
@asteasolutions/zod-to-openapi ^9.1.0, which exports OpenApiGeneratorV32,
and openapi3-ts 4.6.0 ships the oas32 types. So 3.2 only needed wiring.

Add getOpenAPI32Document() and doc32() alongside the existing 3.0 and 3.1
pairs, backed by OpenApiGeneratorV32 and typed with openapi3-ts/oas32.
Base path handling and generator options behave as they do for 3.1.

This makes the 3.2 tag hierarchy (parent, summary, kind) usable, which is
what replaces the x-tagGroups extension for sidebar grouping.

doc32 mirrors doc31, so it carries the same two `any` boundaries; the
eslint suppression counts for index.ts are bumped by one each to match.

Closes honojs#2039
@changeset-bot

changeset-bot Bot commented Sep 8, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 3208405

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@hono/zod-openapi Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[zod-openapi] OpenAPI 3.2 support is now unblocked upstream (zod-to-openapi v9)

1 participant