zod-openapi

Generate OpenAPI 3.x documentation directly from your existing Zod schemas.

Library
npm
v6.0.2
653 stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum. How we score it →
60 /100 Good
Development Activity 40
Maintenance 64
Community 44
Maturity 52
Momentum 40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation. How we score it →
80 /100 Excellent
Architecture 82
Code Quality 88
Innovation 74
Learning Curve 75

zod-openapi converts Zod schemas into OpenAPI 3.1 (and 3.0-compatible) documentation without requiring a separate schema definition layer. Instead of maintaining Zod validators and OpenAPI specs as two parallel sources of truth, it reads metadata attached via Zod v4’s native .meta() method and renders the equivalent JSON Schema-based OpenAPI output, distinguishing between input contexts (request bodies, parameters) and output contexts (responses, headers) since the two can legitimately differ for the same logical schema.

The library exposes two entry points: createDocument, which builds a full OpenAPIObject from paths, webhooks, and components in one call, and createSchema, which converts a single Zod schema in isolation. Both understand Zod’s component registry, so schemas tagged with an id are automatically hoisted into components.schemas and referenced via $ref rather than inlined repeatedly. An override hook (object or function form) lets callers patch the generated JSON Schema for cases the automatic conversion cannot express, such as custom format values or restructuring anyOf into oneOf.

Because it builds on Zod 4’s built-in metadata and JSON Schema conversion rather than monkey-patching the Zod prototype (the approach used by earlier community solutions), it has no runtime side effects on unrelated Zod usage and stays compatible with Zod’s own release cadence. A companion Fastify plugin and ESLint plugin extend the same schema-to-OpenAPI approach into route registration and editor tooling.

What You Get

  • createDocument() — builds a complete OpenAPI 3.1 document from paths, webhooks, and components in a single call, given Zod schemas for request params, request bodies, and responses
  • createSchema() — converts a single Zod schema into its OpenAPI JSON Schema representation, for callers who only need partial output
  • Native .meta() integration — attach id, param, header, override, and outputId metadata directly on Zod schemas with no prototype patching or wrapper functions
  • Automatic component registration and deduplication — schemas tagged with an id are hoisted into components.schemas and referenced by $ref instead of being inlined at every use site
  • Input/output schema differentiation — the same logical schema can render differently for a request body versus a response body (e.g. additionalProperties: false only in outputs), generating separate components automatically when needed
  • Cycle and schema-reuse handling — configurable via cycles (ref/throw) and reused (ref/inline) options for self-referential or shared schemas
  • override hooks — both a shallow object-merge form and a deep function form for patching generated JSON Schema output post-conversion
  • First-class support for parameters, request bodies, responses, headers, callbacks, links, security schemes, and path items as OpenAPI components

Common Use Cases

  • Generating and publishing an OpenAPI/Swagger spec for a TypeScript API that already uses Zod for request/response validation, without maintaining a second spec by hand
  • Feeding an auto-generated OpenAPI document into Redoc, Swagger UI, or an SDK/type generator as part of a CI pipeline
  • Keeping request and response schemas as reusable, named OpenAPI components (components.schemas) shared across many route definitions
  • Building typed API clients or contract tests from a spec that is guaranteed to match the server’s actual runtime validation, because both derive from the same Zod schema

Under The Hood

Architecture

createDocument orchestrates the conversion in three stages: createRegistry (components.ts) builds a ComponentRegistry that tracks separate input and output schema maps keyed by component id, createPaths (paths.ts) walks the supplied paths/webhooks and calls createOperation per HTTP method to resolve parameters, request bodies, and responses against that registry, and createComponents assembles the final components.schemas/parameters/headers/etc. from whatever the registry accumulated. Individual schema conversion is delegated to schema/schema.ts, which walks Zod v4’s $ZodType definitions into JSON Schema. The registry’s dedup-by-id, split-by-input/output-context design is the load-bearing abstraction: content.ts, headers.ts, parameters.ts, links.ts, and callbacks.ts all read from the same registry to decide between inlining a schema and emitting a $ref, so any change to that data structure ripples through every one of those modules.

Tech Stack

A TypeScript pnpm workspace with an internal packages/openapi3-ts package supplying typed OAS 3.0/3.1/3.2 interfaces, built with tsdown into dual ESM/CJS output via the package.json exports map, tested with Vitest, and released through Changesets. The peer dependency is zod ^4.0.0 — the library builds entirely on Zod’s own .meta() metadata and JSON Schema conversion rather than adding a parallel dependency. Tooling (build/lint/test scripts, release provenance) is wrapped by skuba, an internal SEEK build-tooling framework.

Code Quality

45 *.test.ts files sit alongside their implementation files, covering document.ts, paths.ts, components.ts, headers.ts, parameters.ts, callbacks.ts, links.ts, and object.ts with Vitest describe/it suites. ESLint (with a companion eslint-plugin-zod-openapi devDependency) and Prettier enforce style, GitHub Actions runs test and release workflows on every push, and the codebase is strict TypeScript throughout with no untyped escape hatches observed in the core modules read.

What Makes It Unique

Instead of monkey-patching Zod’s prototype — the approach older Zod-to-OpenAPI tools relied on — this library builds directly on Zod v4’s native .meta() and built-in JSON Schema conversion, so it has no side effects on unrelated Zod usage and tracks Zod’s own version support automatically. Its explicit splitting of input versus output component schemas for the same logical Zod type (since a schema can legitimately render differently in a request versus a response) is a design choice most comparable libraries skip, and the override function’s contextual callback (receiving the generated JSON Schema, the source Zod type, and whether it’s an input or output context) gives finer post-processing control than typical override mechanisms.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers