### Full Example Source: https://github.com/asteasolutions/zod-to-openapi/blob/master/README.md Provides links to a full example code and its corresponding YAML representation. ```APIDOC ### A full example A full example code can be found [here](./example/index.ts). And the YAML representation of its result - [here](./example/openapi-docs.yml) ``` -------------------------------- ### Install zod-to-openapi Source: https://github.com/asteasolutions/zod-to-openapi/blob/master/README.md Install the zod-to-openapi library using npm or yarn. ```shell npm install @asteasolutions/zod-to-openapi # or yarn add @asteasolutions/zod-to-openapi ``` -------------------------------- ### Using require Syntax with extendZodWithOpenApi Source: https://github.com/asteasolutions/zod-to-openapi/blob/master/README.md Demonstrates the usage of the 'require' syntax for importing and applying the extendZodWithOpenApi function, followed by starting a server. ```typescript import { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi'; import { z } from 'zod'; extendZodWithOpenApi(z); const { startServer } = require('./server/start'); startServer(); ``` -------------------------------- ### Initializing OpenAPIRegistry and Generator Source: https://github.com/asteasolutions/zod-to-openapi/blob/master/README.md Shows the basic setup for using OpenAPIRegistry to collect definitions and OpenApiGeneratorV3 to generate OpenAPI components. ```typescript import { OpenAPIRegistry, OpenApiGeneratorV3, } from '@asteasolutions/zod-to-openapi'; const registry = new OpenAPIRegistry(); // Register definitions here const generator = new OpenApiGeneratorV3(registry.definitions); return generator.generateComponents(); ``` -------------------------------- ### Defining Custom Components Source: https://github.com/asteasolutions/zod-to-openapi/blob/master/README.md Explains how to define components that are not OpenAPI schemas, such as security schemes and response headers, with a reference to an example test file. ```APIDOC ### Defining custom components You can define components that are not OpenAPI schemas, including security schemes, response headers and others. See [this test file](spec/custom-components.spec.ts) for examples. ``` -------------------------------- ### Running zod-to-openapi with tsx Source: https://github.com/asteasolutions/zod-to-openapi/blob/master/README.md Example of how to execute a TypeScript file containing zod-to-openapi configurations using tsx, including the necessary import for extending Zod. ```typescript //zod-extend.ts import { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi'; import { z } from 'zod'; extendZodWithOpenApi(z); // package.json "scripts": { "start": "tsx --import ./zod-extend.ts ./index.ts", ``` -------------------------------- ### Basic extendZodWithOpenApi Usage Source: https://github.com/asteasolutions/zod-to-openapi/blob/master/README.md Provides the fundamental setup for using zod-to-openapi by extending Zod with the openapi functionality and applying metadata to a string schema. ```typescript import { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi'; import { z } from 'zod'; extendZodWithOpenApi(z); // We can now use `.openapi()` to specify OpenAPI metadata z.string().openapi({ description: 'Some string' }); ``` -------------------------------- ### z.meta() Source: https://context7.com/asteasolutions/zod-to-openapi/llms.txt An alternative to the `.openapi()` method for attaching metadata, leveraging Zod's native `.meta()` function. This method is read transparently by the library starting from v8 (Zod v4), and the `id` property maps to the registration name. No explicit call to `extendZodWithOpenApi` is required when using only `.meta()`. ```APIDOC ## z.meta({ id, ... }) (alternative to `.openapi()`) ### Description Starting from v8 (Zod v4), Zod's native `.meta()` is read transparently. The `id` property maps to the registration name. No call to `extendZodWithOpenApi` is required when using only `.meta()`. ### Usage ```ts import { z } from 'zod'; import { OpenApiGeneratorV3 } from '@asteasolutions/zod-to-openapi'; // Equivalent to z.string().openapi('Username', { description: 'Login handle', example: 'alice' }) const UsernameSchema = z .string() .meta({ id: 'Username', description: 'Login handle', example: 'alice' }); const generator = new OpenApiGeneratorV3([UsernameSchema]); const { components } = generator.generateComponents(); // components.schemas.Username => { type: 'string', description: 'Login handle', example: 'alice' } ``` ``` -------------------------------- ### Define and Register Zod Schema for OpenAPI Source: https://github.com/asteasolutions/zod-to-openapi/blob/master/README.md Define a Zod schema for a User object and register it with a path operation. This example demonstrates how to use the `.openapi()` method to specify schema properties and register the schema for use in responses. ```typescript const UserSchema = z .object({ id: z.string().openapi({ example: '1212121' }), name: z.string().openapi({ example: 'John Doe' }), age: z.number().openapi({ example: 42 }), }) .openapi('User'); registry.registerPath({ method: 'get', path: '/users/{id}', summary: 'Get a single user', request: { params: z.object({ id: z.string() }), }, responses: { 200: { description: 'Object with user data.', content: { 'application/json': { schema: UserSchema, }, }, }, }, }); ``` -------------------------------- ### Using Zod's Native .meta() for OpenAPI Source: https://context7.com/asteasolutions/zod-to-openapi/llms.txt Starting from v8, Zod's native `.meta()` is read transparently, mapping the `id` property to the registration name. This approach does not require calling `extendZodWithOpenApi`. ```typescript import { z } from 'zod'; import { OpenApiGeneratorV3 } from '@asteasolutions/zod-to-openapi'; // Equivalent to z.string().openapi('Username', { description: 'Login handle', example: 'alice' }) const UsernameSchema = z .string() .meta({ id: 'Username', description: 'Login handle', example: 'alice' }); const generator = new OpenApiGeneratorV3([UsernameSchema]); const { components } = generator.generateComponents(); // components.schemas.Username => { type: 'string', description: 'Login handle', example: 'alice' } ``` -------------------------------- ### Automatic Schema Registration with .openapi() Source: https://github.com/asteasolutions/zod-to-openapi/blob/master/README.md Schemas are automatically registered when referenced using the `.openapi()` method. This example shows how a nested schema is registered as 'Test' and the main schema as 'Object'. ```typescript const schema = z.object({ key: z.string().openapi('Test') }).openapi('Object'); ``` -------------------------------- ### Extending Registered Schemas with allOf Source: https://context7.com/asteasolutions/zod-to-openapi/llms.txt When a registered schema is extended using `.extend()`, the generated OpenAPI output uses `allOf` to reference the base schema and include new properties. This example shows extending a base schema to create a User schema. ```typescript import { OpenApiGeneratorV3, extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi'; import { z } from 'zod'; extendZodWithOpenApi(z); const BaseSchema = z.object({ id: z.string(), createdAt: z.date() }).openapi('Base'); // Extended schema references Base via allOf const UserSchema = BaseSchema.extend({ name: z.string(), email: z.string().email(), }).openapi('User'); const { components } = new OpenApiGeneratorV3([BaseSchema, UserSchema]).generateComponents(); /* components.schemas.User => { allOf: [ { $ref: '#/components/schemas/Base' }, { type: 'object', properties: { name: { type: 'string' }, email: { type: 'string', format: 'email' } }, required: ['name','email'] } ] } */ ``` -------------------------------- ### Adding to Build Process Source: https://github.com/asteasolutions/zod-to-openapi/blob/master/README.md Provides guidance on integrating the OpenAPI generation into a project's build process, including creating a script to execute the generation and write the output to a file. ```APIDOC #### Adding it as part of your build In a file inside your project you can have a file like so: ```ts export const registry = new OpenAPIRegistry(); export function generateOpenAPI() { const config = {...}; // your config comes here return new OpenApiGeneratorV3(registry.definitions).generateDocument(config); } ``` You then use the exported `registry` object to register all schemas, parameters and routes where appropriate. Then you can create a script that executes the exported `generateOpenAPI` function. This script can be executed as a part of your build step so that it can write the result to some file like `openapi-docs.json`. ``` -------------------------------- ### Defining Parameter Metadata with .openapi Source: https://github.com/asteasolutions/zod-to-openapi/blob/master/README.md Illustrates how to define parameter metadata for a route using Zod's .openapi() extension, including schema-level and parameter-level descriptions. ```typescript registry.registerPath({ // ... request: { query: z.object({ name: z.string().openapi({ description: 'Schema level description', param: { description: 'Param level description' }, }), }), }, }); ``` -------------------------------- ### Using Schemas vs. Registry Source: https://github.com/asteasolutions/zod-to-openapi/blob/master/README.md Compares the automatic schema registration when using `.openapi` with direct schema definitions versus explicitly registering schemas with an `OpenAPIRegistry` instance. ```APIDOC ### Using schemas vs a registry Schemas are automatically being registered when referenced. That means that if you have a schema like: ```ts const schema = z.object({ key: z.string().openapi('Test') }).openapi('Object'); ``` you'd have the following resulting structure: ```yaml components: schemas: Test: type: 'string', Object: type: 'object', properties: key: $ref: '#/components/schemas/Test' required: ['key'] ``` This does not require any usages of an `OpenAPIRegistry` instance. However the same output can be achieved with the following code: ```ts const registry = new OpenAPIRegistry(); const schema = registry.register( 'Object', z.object({ key: z.string().openapi('Test') }) ); ``` The main benefit of the `.registry` method is that you can use the registry as a "collection" where you would put all such schemas. With `.openapi`: ```ts // file1.ts export const Schema1 = ... // file2.ts export const Schema2 = ... new OpenApiGeneratorV3([Schema1, Schema2]) ``` Adding a `NewSchema` into `file3.ts` would require you to pass that schema manually into the array of the generator constructor. Note: If a `NewSchema` is referenced by any other schemas or a route/webhook definition it would still appear in the resulting document. With `registry.register`: ```ts // registry.ts export const registry = new OpenAPIRegistry() // file1.ts export const Schema1 = registry.register(...) // file2.ts export const Schema2 = registry.register(...) new OpenApiGeneratorV3(registry.definitions) ``` Adding a `NewSchema` into `file3.ts` and using `registry.register` would NOT require you to do any changes to the generator constructor. ``` -------------------------------- ### Defining Route Parameters Source: https://github.com/asteasolutions/zod-to-openapi/blob/master/README.md Demonstrates how to define route parameters separately using `registerParameter` and reference them in a path definition. It also shows the equivalent YAML structure. ```APIDOC ## Defining Route Parameters If you don't want to inline all parameter definitions, you can define them separately with `registerParameter` and then reference them: ```ts const UserIdParam = registry.registerParameter( 'UserId', z.string().openapi({ param: { name: 'id', in: 'path', }, example: '1212121', }) ); registry.registerPath({ ... request: { params: z.object({ id: UserIdParam }), }, responses: ... }); ``` The YAML equivalent would be: ```yaml components: parameters: UserId: in: path name: id schema: type: string example: '1212121' required: true '/users/{id}': get: ... parameters: - $ref: '#/components/parameters/UserId' responses: ... ``` Note: In order to define properties that apply to the parameter itself, use the `param` property of `.openapi`. Any properties provided outside of `param` would be applied to the schema for this parameter. ``` -------------------------------- ### Using Zod .meta for OpenAPI Metadata Source: https://github.com/asteasolutions/zod-to-openapi/blob/master/README.md Demonstrates using Zod's .meta() to provide OpenAPI metadata, which is read by zod-to-openapi. This is an alternative to using .openapi(). ```typescript const schema = z .string() .openapi('Schema', { description: 'Name of the user', example: 'Test' }); const schema2 = z .string() .meta({ id: 'Schema2', description: 'Name of the user', example: 'Test' }); ``` -------------------------------- ### Generating the Full Document Source: https://github.com/asteasolutions/zod-to-openapi/blob/master/README.md Shows how to generate a complete OpenAPI document using the `generateDocument` method of `OpenApiGeneratorV3` or `OpenApiGeneratorV31`. ```APIDOC ## Generating the Full Document A full OpenAPI document can be generated using the `generateDocument` method of an `OpenApiGeneratorV3` or `OpenApiGeneratorV31` instance. It takes one argument - the document config. It may look something like this: ```ts return generator.generateDocument({ openapi: '3.0.0', info: { version: '1.0.0', title: 'My API', description: 'This is the API', }, servers: [{ url: 'v1' }], }); ``` ``` -------------------------------- ### OpenAPIRegistry Constructor Source: https://context7.com/asteasolutions/zod-to-openapi/llms.txt Initializes a new OpenAPIRegistry. It can optionally accept an array of parent registries to inherit definitions from. ```APIDOC ## `new OpenAPIRegistry(parents?)` A collection that stores schema, parameter, path, webhook, and raw component definitions. Accepts an optional array of parent registries whose definitions are merged in when `.definitions` is accessed. ```ts import { OpenAPIRegistry } from '@asteasolutions/zod-to-openapi'; import { z } from 'zod'; const sharedRegistry = new OpenAPIRegistry(); const appRegistry = new OpenAPIRegistry([sharedRegistry]); // inherits shared definitions // All definitions from sharedRegistry are visible through appRegistry.definitions const TokenSchema = sharedRegistry.register( 'AuthToken', z.string().openapi({ example: 'eyJhbGci...' }) ); const UserSchema = appRegistry.register( 'User', z.object({ id: z.string(), token: TokenSchema }) ); console.log(appRegistry.definitions.length); // 2 (AuthToken + User) ``` ``` -------------------------------- ### Registering Multiple Schemas with OpenAPIRegistry Source: https://github.com/asteasolutions/zod-to-openapi/blob/master/README.md Demonstrates registering multiple schemas using an `OpenAPIRegistry` instance. The generator is then initialized with the registry's definitions. ```typescript // registry.ts export const registry = new OpenAPIRegistry() // file1.ts export const Schema1 = registry.register(...) // file2.ts export const Schema2 = registry.register(...) new OpenApiGeneratorV3(registry.definitions) ``` -------------------------------- ### Initialize OpenAPI Generator with Global Options Source: https://github.com/asteasolutions/zod-to-openapi/blob/master/README.md Instantiate the OpenApiGeneratorV3 with registry definitions and global options. Supported global options include `unionPreferredType` and `sortComponents`. ```typescript const generator = new OpenApiGeneratorV3(registry.definitions, options); ``` -------------------------------- ### Initialize OpenAPIRegistry with Parent Registries Source: https://context7.com/asteasolutions/zod-to-openapi/llms.txt Create a new OpenAPIRegistry instance. Optionally, provide an array of parent registries to merge their definitions. Definitions from parent registries are accessible through the child registry. ```typescript import { OpenAPIRegistry } from '@asteasolutions/zod-to-openapi'; import { z } from 'zod'; const sharedRegistry = new OpenAPIRegistry(); const appRegistry = new OpenAPIRegistry([sharedRegistry]); // inherits shared definitions // All definitions from sharedRegistry are visible through appRegistry.definitions const TokenSchema = sharedRegistry.register( 'AuthToken', z.string().openapi({ example: 'eyJhbGci...' }) ); const UserSchema = appRegistry.register( 'User', z.object({ id: z.string(), token: TokenSchema }) ); console.log(appRegistry.definitions.length); // 2 (AuthToken + User) ``` -------------------------------- ### Manual Schema Registration with OpenAPIRegistry Source: https://github.com/asteasolutions/zod-to-openapi/blob/master/README.md Manually register schemas using an `OpenAPIRegistry` instance. This provides a centralized collection for all definitions. ```typescript const registry = new OpenAPIRegistry(); const schema = registry.register( 'Object', z.object({ key: z.string().openapi('Test') }) ); ``` -------------------------------- ### Conclusion on Registry Usage Source: https://github.com/asteasolutions/zod-to-openapi/blob/master/README.md Summarizes the primary benefit of using an `OpenAPIRegistry` instance, which is to include unreferenced schemas in the resulting document. ```APIDOC #### Conclusion Using an `OpenAPIRegistry` instance is mostly useful if you would want your resulting document to contain unreferenced schemas. That can sometimes be useful - for example when you are slowly integrating an already existing documentation with `@asteasolutions/zod-to-openapi` and you are migrating small pieces at a time. Those pieces can then be referenced directly from an existing documentation. ``` -------------------------------- ### Apply One-Off Options to a Single Schema Source: https://github.com/asteasolutions/zod-to-openapi/blob/master/README.md Register a schema with optional metadata and configuration, or simply add metadata. `options` can be passed for specific schema configurations, such as `unionPreferredType`. ```typescript schema.openapi('Schema', metadata, options); // when registering a schema or schema.openapi(metadata, options) // when simply adding some metadata to it ``` -------------------------------- ### Extending Registered Schemas (`allOf` / `anyOf`) Source: https://context7.com/asteasolutions/zod-to-openapi/llms.txt Illustrates how extending a registered Zod schema using `.extend()` results in the generated OpenAPI schema using `allOf` to reference the base schema and incorporate new properties. ```APIDOC ### Extending Registered Schemas (`allOf` / `anyOf`) When a registered schema is extended with `.extend()`, the generated output uses `allOf` to reference the base schema and add new properties. ```ts import { OpenApiGeneratorV3, extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi'; import { z } from 'zod'; extendZodWithOpenApi(z); const BaseSchema = z.object({ id: z.string(), createdAt: z.date() }).openapi('Base'); // Extended schema references Base via allOf const UserSchema = BaseSchema.extend({ name: z.string(), email: z.string().email(), }).openapi('User'); const { components } = new OpenApiGeneratorV3([BaseSchema, UserSchema]).generateComponents(); /* components.schemas.User => { allOf: [ { $ref: '#/components/schemas/Base' }, { type: 'object', properties: { name: { type: 'string' }, email: { type: 'string', format: 'email' } }, required: ['name','email'] } ] } */ ``` ``` -------------------------------- ### Generate OpenAPI Document Source: https://github.com/asteasolutions/zod-to-openapi/blob/master/README.md Generate a full OpenAPI document using `OpenApiGeneratorV3` or `OpenApiGeneratorV31`. Pass the document configuration object to the `generateDocument` method. ```typescript return generator.generateDocument({ openapi: '3.0.0', info: { version: '1.0.0', title: 'My API', description: 'This is the API', }, servers: [{ url: 'v1' }], }); ``` -------------------------------- ### Using the .openapi() Method Overloads Source: https://context7.com/asteasolutions/zod-to-openapi/llms.txt The `.openapi()` method attaches OpenAPI metadata to Zod schemas. It returns a new schema instance without mutating the original. Use it for metadata only, to register a name for reuse via `$ref`, or for both. ```typescript import { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi'; import { z } from 'zod'; extendZodWithOpenApi(z); // Overload 1: metadata only — adds fields to the generated schema object const EmailSchema = z.string().email().openapi({ description: 'RFC-5321 email address', example: 'user@example.com', format: 'email', }); // Overload 2: register name only — schema becomes reusable via $ref const AddressSchema = z.object({ street: z.string(), city: z.string() }).openapi('Address'); // Overload 3: register name + metadata const ProductSchema = z .object({ id: z.string().uuid(), price: z.number().positive() }) .openapi('Product', { description: 'A purchasable product' }); // Per-field parameter metadata (param.* applies to the OpenAPI parameter object, // everything else applies to the schema object inside it) const SortParam = z.enum(['asc', 'desc']).openapi({ description: 'Sort direction for the field', example: 'asc', param: { description: 'Direction of sort', name: 'sort', in: 'query' }, }); // One-off generation option: force oneOf for this union const StatusSchema = z .union([z.literal('active'), z.literal('inactive')]) .openapi({ description: 'Account status' }, { unionPreferredType: 'oneOf' }); ``` -------------------------------- ### Build Integration: Centralized Registry Source: https://github.com/asteasolutions/zod-to-openapi/blob/master/README.md Integrate OpenAPI generation into your build process by exporting a function that uses a centralized `OpenAPIRegistry`. This function can then be called to generate the OpenAPI document and write it to a file. ```typescript export const registry = new OpenAPIRegistry(); export function generateOpenAPI() { const config = {...}; // your config comes here return new OpenApiGeneratorV3(registry.definitions).generateDocument(config); } ``` -------------------------------- ### .openapi() method Source: https://context7.com/asteasolutions/zod-to-openapi/llms.txt Attaches OpenAPI metadata to any Zod schema. This method returns a new schema instance, ensuring the original schema is never mutated. It supports three overloads for attaching metadata, registration names, or both. ```APIDOC ## .openapi() method (three overloads) ### Description Attaches OpenAPI metadata to any Zod schema. The method returns a new schema instance so the original is never mutated. ### Overload 1: Metadata Only Adds fields to the generated schema object without registering a name. ```ts const EmailSchema = z.string().email().openapi({ description: 'RFC-5321 email address', example: 'user@example.com', format: 'email', }); ``` ### Overload 2: Register Name Only Registers a schema with a name, making it reusable via `$ref`. ```ts const AddressSchema = z.object({ street: z.string(), city: z.string() }).openapi('Address'); ``` ### Overload 3: Register Name and Metadata Registers a schema with a name and attaches OpenAPI metadata. ```ts const ProductSchema = z .object({ id: z.string().uuid(), price: z.number().positive() }) .openapi('Product', { description: 'A purchasable product' }); ``` ### Parameter Metadata Metadata for OpenAPI parameters can be attached using the `param` key within the OpenAPI object. ```ts const SortParam = z.enum(['asc', 'desc']).openapi({ description: 'Sort direction for the field', example: 'asc', param: { description: 'Direction of sort', name: 'sort', in: 'query' }, }); ``` ### Union Type Preference Allows specifying the preferred generation type for unions. ```ts const StatusSchema = z .union([z.literal('active'), z.literal('inactive')]) .openapi({ description: 'Account status' }, { unionPreferredType: 'oneOf' }); ``` ``` -------------------------------- ### Registering Components Source: https://context7.com/asteasolutions/zod-to-openapi/llms.txt Register raw OpenAPI components like security schemes, headers, or responses using `registry.registerComponent`. This allows for reusable definitions across your OpenAPI document. ```APIDOC ## `registry.registerComponent(type, name, component)` Registers any raw OpenAPI component object (security schemes, headers, responses, examples, links, callbacks, etc.) without wrapping it in Zod. Returns `{ name, ref }` where `ref` is a ready-to-use `{ $ref: '#/components//' }` object. ### Example ```ts import { OpenAPIRegistry, OpenApiGeneratorV3 } from '@asteasolutions/zod-to-openapi'; const registry = new OpenAPIRegistry(); // Security scheme const bearerAuth = registry.registerComponent('securitySchemes', 'bearerAuth', { type: 'http', scheme: 'bearer', bearerFormat: 'JWT', }); // Reusable header const requestIdHeader = registry.registerComponent('headers', 'X-Request-Id', { required: true, description: 'Unique request identifier for tracing', schema: { type: 'string', example: 'req-abc-123' }, }); // Reusable error response const notFoundResponse = registry.registerComponent('responses', 'NotFound', { description: 'The requested resource was not found', content: { 'application/json': { schema: { type: 'object', properties: { message: { type: 'string' } } }, }, }, }); registry.registerPath({ method: 'get', path: '/items/{id}', security: [{ [bearerAuth.name]: [] }], responses: { 200: { description: 'Item found', headers: { 'x-request-id': requestIdHeader.ref }, content: { 'application/json': { schema: { type: 'object' } } }, }, 404: notFoundResponse.ref, }, }); ``` ``` -------------------------------- ### YAML Output for Automatically Registered Schemas Source: https://github.com/asteasolutions/zod-to-openapi/blob/master/README.md The resulting OpenAPI structure when schemas are automatically registered using `.openapi()`. Note how 'Test' is a schema and 'Object' references it. ```yaml components: schemas: Test: type: 'string' Object: type: 'object' properties: key: $ref: '#/components/schemas/Test' required: ['key'] ``` -------------------------------- ### Registering a path or webhook Source: https://github.com/asteasolutions/zod-to-openapi/blob/master/README.md OpenAPI paths and webhooks are registered using the `registerPath` and `registerWebhook` methods of an `OpenAPIRegistry` instance, respectively. ```APIDOC ## Defining routes & webhooks An OpenAPI path is registered using the `registerPath` method of an `OpenAPIRegistry` instance. An OpenAPI webhook is registered using the `registerWebhook` method and takes the same parameters as `registerPath`. ```ts registry.registerPath({ method: 'get', path: '/users/{id}', description: 'Get user data by its id', summary: 'Get a single user', request: { params: z.object({ id: z.string().openapi({ example: '1212121' }), }), }, responses: { 200: { description: 'Object with user data.', content: { 'application/json': { schema: UserSchema, }, }, }, 204: { description: 'No content - successful operation', }, }, }); ``` The YAML equivalent of the schema above would be: ```yaml '/users/{id}': get: description: Get user data by its id summary: Get a single user parameters: - in: path name: id schema: type: string example: '1212121' required: true responses: '200': description: Object with user data. content: application/json: schema: $ref: '#/components/schemas/User' '204': description: No content - successful operation ``` The library specific properties for `registerPath` are `method`, `path`, `request` and `responses`. Everything else gets directly appended to the path definition. - `method` - One of `get`, `post`, `put`, `delete` and `patch`; - `path` - a string - being the path of the endpoint; - `request` - an optional object with optional `body`, `params`, `query` and `headers` keys, - `query`, `params` - being instances of `ZodObject` - `body` - an object with a `description` and a `content` record where: - the key is a `mediaType` string like `application/json` - and the value is an object with a `schema` of any `zod` type - `headers` - instances of `ZodObject` or an array of any `zod` instances - `responses` - an object where the key is the status code or `default` and the value is an object with a `description` and a `content` record where: - the key is a `mediaType` string like `application/json` - and the value is an object with a `schema` of any `zod` type ``` -------------------------------- ### YAML Equivalent for Route Parameters Source: https://github.com/asteasolutions/zod-to-openapi/blob/master/README.md The YAML representation of a separately defined path parameter, showing how it's structured within the OpenAPI components. ```yaml components: parameters: UserId: in: path name: id schema: type: string example: '1212121' required: true '/users/{id}': get: ...rest parameters: - $ref: '#/components/parameters/UserId' responses: ... ``` -------------------------------- ### Registering Webhooks Source: https://context7.com/asteasolutions/zod-to-openapi/llms.txt Register webhooks using `registry.registerWebhook`. This method is similar to `registerPath` but specifically for webhooks and is only available when using `OpenApiGeneratorV31`. ```APIDOC ## `registry.registerWebhook(routeConfig)` Identical to `registerPath` but registers the definition under `webhooks` in the OpenAPI 3.1 document. Only surfaced when using `OpenApiGeneratorV31`. ### Example ```ts import { OpenAPIRegistry, OpenApiGeneratorV31, extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi'; import { z } from 'zod'; extendZodWithOpenApi(z); const registry = new OpenAPIRegistry(); const OrderSchema = registry.register( 'Order', z.object({ orderId: z.string(), status: z.enum(['pending', 'shipped', 'delivered']) }) ); registry.registerWebhook({ method: 'post', path: 'orderStatusUpdated', // webhook name, not a URL path summary: 'Fires when an order status changes', request: { body: { content: { 'application/json': { schema: OrderSchema } }, }, }, responses: { 200: { description: 'Webhook received' }, }, }); const generator = new OpenApiGeneratorV31(registry.definitions); const doc = generator.generateDocument({ openapi: '3.1.0', info: { title: 'Shop API', version: '1.0.0' } }); // doc.webhooks.orderStatusUpdated.post.requestBody ... ``` ``` -------------------------------- ### Define User Schema with registry.register Source: https://github.com/asteasolutions/zod-to-openapi/blob/master/README.md Alternatively, register a Zod schema using the `register` method of an `OpenAPIRegistry` instance. This approach is useful for managing multiple schemas. ```typescript const UserSchema = registry.register( 'User', z.object({ id: z.string().openapi({ example: '1212121' }), name: z.string().openapi({ example: 'John Doe' }), age: z.number().openapi({ example: 42 }), }) ); const generator = new OpenApiGeneratorV3(registry.definitions); ``` -------------------------------- ### extendZodWithOpenApi Source: https://context7.com/asteasolutions/zod-to-openapi/llms.txt Extends the Zod prototype with the `.openapi()` method. This function must be called once at your application's entry point before any schema is defined. Subsequent calls are safe and have no effect. ```APIDOC ## extendZodWithOpenApi(zod) ### Description Extends the Zod prototype with the `.openapi()` method. Must be called once at your application's entry point before any schema is defined. Calling it multiple times is safe — subsequent calls are no-ops. ### Usage ```ts import { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi'; import { z } from 'zod'; extendZodWithOpenApi(z); // Now every Zod type has .openapi() const NameSchema = z.string().openapi({ description: 'User display name', example: 'Alice' }); const AgeSchema = z.number().int().openapi({ description: 'Age in years', example: 30 }); // Register a named schema (becomes $ref: '#/components/schemas/User') const UserSchema = z.object({ name: NameSchema, age: AgeSchema }).openapi('User'); ``` ``` -------------------------------- ### OpenAPI 3.0.x Generator Source: https://context7.com/asteasolutions/zod-to-openapi/llms.txt Instantiate `OpenApiGeneratorV3` to generate OpenAPI 3.0.x documents. It accepts the registry definitions and optional global configuration options. ```APIDOC ## `new OpenApiGeneratorV3(definitions, options?)` Generator for OpenAPI **3.0.x** documents. Accepts the array from `registry.definitions` (or plain annotated Zod schemas) and an optional global options object. ### Example ```ts import { OpenAPIRegistry, OpenApiGeneratorV3, extendZodWithOpenApi, } from '@asteasolutions/zod-to-openapi'; import { z } from 'zod'; import * as yaml from 'yaml'; extendZodWithOpenApi(z); const registry = new OpenAPIRegistry(); registry.register('Tag', z.string().openapi({ example: 'typescript' })); // Global option: sort components alphabetically + prefer anyOf for unions const generator = new OpenApiGeneratorV3(registry.definitions, { sortComponents: 'alphabetically', unionPreferredType: 'anyOf', }); // Generate just the components section (no paths) const { components } = generator.generateComponents(); // Generate the full document const document = generator.generateDocument({ openapi: '3.0.0', info: { title: 'My API', version: '2.0.0', description: 'API description' }, servers: [{ url: 'https://api.example.com/v2' }], }); // Serialize to YAML console.log(yaml.stringify(document)); ``` ``` -------------------------------- ### generator.generateDocument(config) Source: https://context7.com/asteasolutions/zod-to-openapi/llms.txt Generates a complete OpenAPI document object by combining configuration with generated components, paths, and webhooks. The output is a plain JavaScript object that needs to be serialized. ```APIDOC ## `generator.generateDocument(config)` Produces a complete OpenAPI document object combining the provided top-level config with generated `components`, `paths`, and (for 3.1.x) `webhooks`. The result is a plain JS object — serialize it yourself with `JSON.stringify` or a YAML library. ```ts import { OpenAPIRegistry, OpenApiGeneratorV3, extendZodWithOpenApi, } from '@asteasolutions/zod-to-openapi'; import { z } from 'zod'; import * as fs from 'fs'; import * as yaml from 'yaml'; extendZodWithOpenApi(z); const registry = new OpenAPIRegistry(); const UserSchema = registry.register('User', z.object({ id: z.string(), name: z.string() })); registry.registerPath({ method: 'get', path: '/users', responses: { 200: { description: 'List of users', content: { 'application/json': { schema: z.array(UserSchema) } } } }, }); const doc = new OpenApiGeneratorV3(registry.definitions).generateDocument({ openapi: '3.0.0', info: { title: 'Users API', version: '1.0.0' }, servers: [{ url: '/api' }], }); fs.writeFileSync('openapi.json', JSON.stringify(doc, null, 2)); fs.writeFileSync('openapi.yaml', yaml.stringify(doc)); ``` ``` -------------------------------- ### Extend Zod with OpenAPI Functionality Source: https://github.com/asteasolutions/zod-to-openapi/blob/master/README.md Call `extendZodWithOpenApi()` once in your project's entry point to enable the `.openapi()` method on Zod objects. This is necessary for using OpenAPI-specific features. ```typescript import { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi'; extendZodWithOpenApi(); ``` -------------------------------- ### OpenAPI Generator V3 vs V3.1 Nullable Differences Source: https://github.com/asteasolutions/zod-to-openapi/blob/master/README.md Compares how OpenAPI Generator V3 and V3.1 handle nullable types. V3.1 uses type arrays, while V3 uses a 'nullable: true' property. ```yaml # 3.1.0 # nullable is invalid in 3.1.0 but type arrays are invalid in previous versions name: type: - 'string' - 'null' # 3.0.0 name: type: 'string' nullable: true ``` -------------------------------- ### Supported Zod Schema Types Source: https://context7.com/asteasolutions/zod-to-openapi/llms.txt Demonstrates the mapping of various Zod schema types to their OpenAPI equivalents, including primitives, combinators, enums, arrays, records, tuples, unions, discriminated unions, intersections, and objects with catchall or strict properties. ```APIDOC ### Supported Zod Schema Types The library handles the full range of Zod primitives and combinators. Below shows key type mappings: ```ts import { OpenApiGeneratorV3, extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi'; import { z } from 'zod'; extendZodWithOpenApi(z); const schemas = [ z.string().email().openapi('Email'), z.string().uuid().openapi('Uuid'), z.string().datetime().openapi('Timestamp'), z.number().int().min(0).openapi('PositiveInt'), z.bigint().openapi('BigInt'), z.boolean().openapi('Flag'), z.date().openapi('DateField'), z.enum(['a', 'b', 'c']).openapi('SimpleEnum'), z.nativeEnum({ X: 1, Y: 2 } as const).openapi('NativeEnum'), z.array(z.string()).min(1).openapi('StringList'), z.record(z.string(), z.number()).openapi('StringToNumber'), z.tuple([z.string(), z.number()]).openapi('Pair'), z.union([z.string(), z.number()]).openapi('StringOrNumber'), z.discriminatedUnion('kind', [ z.object({ kind: z.literal('a'), val: z.string() }).openapi('KindA'), z.object({ kind: z.literal('b'), val: z.number() }).openapi('KindB'), ]).openapi('DiscUnion'), z.intersection( z.object({ a: z.string() }).openapi('ObjA'), z.object({ b: z.number() }).openapi('ObjB') ).openapi('Combined'), z.object({ x: z.string() }).catchall(z.unknown()).openapi('OpenObject'), z.object({ x: z.string() }).strict().openapi('ClosedObject'), // additionalProperties: false ]; const { components } = new OpenApiGeneratorV3(schemas).generateComponents(); ``` ``` -------------------------------- ### Generating OpenAPI 3.0.x Documents with `OpenApiGeneratorV3` Source: https://context7.com/asteasolutions/zod-to-openapi/llms.txt Use `OpenApiGeneratorV3` to generate OpenAPI 3.0.x documents. It accepts registry definitions and optional global configuration options like sorting components or preferred union types. ```typescript import { OpenAPIRegistry, OpenApiGeneratorV3, extendZodWithOpenApi, } from '@asteasolutions/zod-to-openapi'; import { z } from 'zod'; import * as yaml from 'yaml'; extendZodWithOpenApi(z); const registry = new OpenAPIRegistry(); registry.register('Tag', z.string().openapi({ example: 'typescript' })); // Global option: sort components alphabetically + prefer anyOf for unions const generator = new OpenApiGeneratorV3(registry.definitions, { sortComponents: 'alphabetically', unionPreferredType: 'anyOf', }); // Generate just the components section (no paths) const { components } = generator.generateComponents(); // Generate the full document const document = generator.generateDocument({ openapi: '3.0.0', info: { title: 'My API', version: '2.0.0', description: 'API description' }, servers: [{ url: 'https://api.example.com/v2' }], }); // Serialize to YAML console.log(yaml.stringify(document)); ``` -------------------------------- ### Generate Full OpenAPI Document Source: https://context7.com/asteasolutions/zod-to-openapi/llms.txt Use `generateDocument` to create a complete OpenAPI document object. Serialize the resulting JS object to JSON or YAML. ```typescript import { OpenAPIRegistry, OpenApiGeneratorV3, extendZodWithOpenApi, } from '@asteasolutions/zod-to-openapi'; import { z } from 'zod'; import * as fs from 'fs'; import * as yaml from 'yaml'; extendZodWithOpenApi(z); const registry = new OpenAPIRegistry(); const UserSchema = registry.register('User', z.object({ id: z.string(), name: z.string() })); registry.registerPath({ method: 'get', path: '/users', responses: { 200: { description: 'List of users', content: { 'application/json': { schema: z.array(UserSchema) } } } }, }); const doc = new OpenApiGeneratorV3(registry.definitions).generateDocument({ openapi: '3.0.0', info: { title: 'Users API', version: '1.0.0' }, servers: [{ url: '/api' }], }); fs.writeFileSync('openapi.json', JSON.stringify(doc, null, 2)); fs.writeFileSync('openapi.yaml', yaml.stringify(doc)); ``` -------------------------------- ### Extending Registered Schemas with anyOf Source: https://github.com/asteasolutions/zod-to-openapi/blob/master/README.md Shows how to extend a registered Zod schema and have the extended schema use 'anyOf' with a reference to the original schema. Requires using .openapi(). ```typescript const schema = z.object({ name: z.string() }).openapi('Schema'); const schema2 = schema.extend({ age: z.number() }).openapi('Schema2'); ``` -------------------------------- ### Defining Schemas Source: https://github.com/asteasolutions/zod-to-openapi/blob/master/README.md Schemas can be defined using the `.openapi` method on Zod objects or by using the `register` method of an `OpenAPIRegistry` instance. ```APIDOC ## Defining Schemas An OpenApi schema should be registered by using the `.openapi` method and providing a name: ```ts const UserSchema = z .object({ id: z.string().openapi({ example: '1212121' }), name: z.string().openapi({ example: 'John Doe' }), age: z.number().openapi({ example: 42 }), }) .openapi('User'); const generator = new OpenApiGeneratorV3([UserSchema]); ``` The same can be achieved by using the `register` method of an `OpenAPIRegistry` instance. ```ts const UserSchema = registry.register( 'User', z.object({ id: z.string().openapi({ example: '1212121' }), name: z.string().openapi({ example: 'John Doe' }), age: z.number().openapi({ example: 42 }), }) ); const generator = new OpenApiGeneratorV3(registry.definitions); ``` If run now, `generator.generateComponents()` will generate the following structure: ```yaml components: schemas: User: type: object properties: id: type: string example: '1212121' name: type: string example: John Doe age: type: number example: 42 required: - id - name - age ``` The key for the schema in the output is the first argument passed to `.openapi` method (or the `.register`) - in this case: `User`. Note that `generateComponents` does not return YAML but a JS object - you can then serialize that object into YAML or JSON depending on your use-case. The resulting schema can then be referenced by using `$ref: #/components/schemas/User` in an existing OpenAPI JSON. This will be done automatically for Routes defined through the registry. Note by default a Zod object will result in `"additionalProperties": true` as per the Open API spec unless using `strict` or `catchall`, this is in contrast to normal Zod object usage where `zod.parse` is used. ``` -------------------------------- ### Register Path with registry.registerPath Source: https://github.com/asteasolutions/zod-to-openapi/blob/master/README.md Register an OpenAPI path using the `registerPath` method. This method takes details about the HTTP method, path, request parameters, and responses. ```typescript registry.registerPath({ method: 'get', path: '/users/{id}', description: 'Get user data by its id', summary: 'Get a single user', request: { params: z.object({ id: z.string().openapi({ example: '1212121' }), }), }, responses: { 200: { description: 'Object with user data.', content: { 'application/json': { schema: UserSchema, }, }, }, 204: { description: 'No content - successful operation', }, }, }); ```