zod-to-openapi

repository·master·Indexed 23 days ago

https://github.com/asteasolutions/zod-to-openapi

A library that builds OpenAPI (Swagger) schemas from Zod schemas, allowing developers to maintain a single source of truth for API validation and documentation. It provides tools to extend Zod with an `.openapi()` method for metadata, an `OpenAPIRegistry` to collect definitions, and generators for OpenAPI versions 3.0.x, 3.1.x, and 3.2.x.

Tokens
5.8K
Snippets
14
Records
43
Agent score
81%

What's inside @asteasolutions/zod-to-openapi

  1. Generate OpenAPI documentation from Zod schemas

    master

    The zod-to-openapi library allows you to use Zod schemas as a single source of truth for both runtime validation and OpenAPI (Swagger) documentation. By extending Zod schemas with .openapi() metadata, you can define examples, descriptions, and component names that are then used to generate a complete OpenAPI specification.

    This eliminates the need to maintain separate validation logic and documentation files, ensuring they stay in sync.

    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,
            },
          },
        },
      },
    });
  2. Use OpenAPIRegistry to collect definitions

    master
    The OpenAPIRegistry is a utility used to collect schemas, parameters, and routes. This is the recommended way to manage definitions, especially if you want to include unreferenced schemas in your final document or avoid manually passing arrays of schemas to the generator.
  3. Initialize Zod with OpenAPI support

    master

    To use the .openapi() method on Zod objects, you must call extendZodWithOpenApi(z) exactly once in a common entrypoint file of your project (e.g., index.ts or app.ts).

    If you are using Webpack with tree-shaking, ensure the entrypoint file is marked as having side-effects.

    import { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi';
    import { z } from 'zod';
    
    extendZodWithOpenApi(z);
  4. Configure generator options

    master

    You can customize schema generation using global options passed to the generator constructor or one-off options passed to .openapi().

    Global Options:

    • unionPreferredType: 'oneOf' | 'anyOf' — Configures how Zod unions are generated.
    • sortComponents: 'alphabetically' — Sorts schemas and parameters alphabetically.

    One-off Options (via .openapi()):

    • unionPreferredType: 'oneOf' | 'anyOf'
  5. Generate OpenAPI documentation using OpenApiGenerator

    master

    Once you have registered your schemas and routes in an OpenAPIRegistry, use one of the OpenApiGenerator classes to produce a valid OpenAPI specification. The library provides generators for different versions of the OpenAPI specification:

    • OpenApiGeneratorV3: For OpenAPI 3.0.0
    • OpenApiGeneratorV31: For OpenAPI 3.1.0
    • OpenApiGeneratorV32: For OpenAPI 3.2.0
  6. Extend Zod with OpenAPI support using extendZodWithOpenApi

    master

    To use the .openapi() method on Zod schemas, you must first extend your Zod instance using extendZodWithOpenApi. This function modifies the ZodType prototype to include OpenAPI metadata capabilities.

    import { z } from 'zod';
    import { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi';
    
    extendZodWithOpenApi(z);
    
    // Now you can use .openapi() on any schema
    const schema = z.string().openapi({ example: 'hello' });
  7. Workaround for nullable registered schemas

    master

    A known limitation exists where z.nullable(schema) may not generate a $ref for the underlying registered schema.

    Workaround: Use schema.nullable() instead. This is functionally identical in Zod but is fully supported by the library and ensures correct $ref generation.

  8. Register reusable parameters

    master

    To avoid inlining parameter definitions, use registry.registerParameter. You can then reference the returned parameter object within a registerPath request.

    const UserIdParam = registry.registerParameter(
      'UserId',
      z.string().openapi({
        param: {
          name: 'id',
          in: 'path',
        },
        example: '1212121',
      })
    );
    
    registry.registerPath({
      method: 'get',
      path: '/users/{id}',
      request: {
        params: z.object({
          id: UserIdParam,
        }),
      },
      responses: { /* ... */ },
    });
  9. Choose the correct OpenApiGenerator version

    master

    There are three generators that follow the same interface but target different OpenAPI versions:

    • OpenApiGeneratorV3: Targets OpenAPI 3.0.x.
    • OpenApiGeneratorV31: Targets OpenAPI 3.1.x (uses type arrays for nullables instead of nullable: true).
    • OpenApiGeneratorV32: Targets OpenAPI 3.2.x (supports itemSchema for streaming media types).

    Both generators accept an array of definitions in their constructor and provide two main methods:

    • generateComponents(): Returns only the /components section (schemas, parameters, etc.) as a JavaScript object.
    • generateDocument(): Returns the full OpenAPI document (including paths/routes) based on a configuration object.
    // Example: Generating a full document for 3.0.0
    const generator = new OpenApiGeneratorV3(registry.definitions);
    const document = generator.generateDocument({
      openapi: '3.0.0',
      info: {
        version: '1.0.0',
        title: 'My API',
      },
      servers: [{ url: 'v1' }],
    });