openapi-zod-client

repository·main·Indexed 22 days ago

https://github.com/astahmer/openapi-zod-client

A tool that generates a Zodios API client—a TypeScript HTTP client with Zod runtime validation—from OpenAPI v3 and above specifications (JSON/YAML). It can be used as a CLI for CI/CD automation or programmatically via the `generateZodClientFromOpenAPI` function. Features include support for custom Handlebars templates, Prettier formatting, endpoint grouping strategies, and configurable success/error status expressions.

Tokens
5.5K
Snippets
10
Records
21
Agent score
78%

What's inside openapi-zod-client

  1. Customize success and error status expressions

    main

    By default, the CLI determines which OpenAPI response is the 'main' success response and which are errors. You can override this behavior using --success-expr and --error-expr. These expressions are evaluated using whence and work similarly to Axios's validateStatus.

    • --success-expr <expr>: Determines which response status is the main success status for ZodiosEndpoint["response"].
    • --error-expr <expr>: Determines which response status is considered an error for ZodiosEndpoint["errors"].

    Example: To treat all 2xx status codes as success:

    --success-expr "status >= 200 && status < 300"
  2. Install and use openapi-zod-client via CLI

    main

    You can use openapi-zod-client to generate a zodios API client (a TypeScript HTTP client with Zod validation) from an OpenAPI (JSON/YAML) specification. This is useful when you consume APIs from other teams where you only have the OpenAPI spec as the source of truth.

    Local Installation

    Install as a dev dependency and run via pnpm:

    pnpm i -D openapi-zod-client
    pnpm openapi-zod-client "./input/file.json" -o "./output/client.ts"

    Direct Execution (No Install)

    Run directly using pnpx:

    pnpx openapi-zod-client "./input/file.yaml" -o "./output/client.ts"

    Tips

    • Default Output: If you omit the -o flag, the tool defaults to <input_filename>.ts.
    • URL Input: You can pass a URL instead of a local file path:
      pnpx openapi-zod-client https://example.com/openapi.yaml -o ./client.ts
    • Multi-file Specs: $ref pointing to other files is supported. If you encounter issues, try dereferencing your document first.
    pnpm openapi-zod-client "./input/file.json" -o "./output/client.ts"
  3. Customize output with Handlebars templates and Prettier

    main

    If the default output doesn't meet your needs, you can provide a custom Handlebars template and a custom Prettier configuration to control exactly how the TypeScript code is generated and formatted.

    Example command:

    pnpm openapi-zod-client ./example/petstore.yaml \
      -o ./example/petstore-schemas.ts \
      -t ./example/schemas-only.hbs \
      -p ./example/prettier-custom.json \
      --export-schemas
    pnpm openapi-zod-client ./example/petstore.yaml -o ./example/petstore-schemas.ts -t ./example/schemas-only.hbs -p ./example/prettier-custom.json --export-schemas
  4. Understand the endpoint definition structure

    main

    The core extraction logic produces an EndpointDefinitionWithRefs object for every operation in the OpenAPI document. This object is used to drive the generation of the Zodios client code.

    An endpoint definition contains:

    • method: The HTTP method (e.g., get, post).
    • path: The URL path (with hyphens replaced).
    • alias: The operation name or generated identifier.
    • description: The operation description.
    • requestFormat: The format of the body (e.g., json, binary, form-url, form-data, or text).
    • parameters: An array of parameters (Path, Query, or Header) including their name, type, and the Zod schema string.
    • response: A string representing the Zod schema for the primary successful response.
    • errors: An array of error objects containing the status, description, and the Zod schema string for non-2xx responses.
    • responses (optional): If withAllResponses is enabled, contains all response status codes and their schemas.
  5. OpenAPI Version Compatibility

    main

    The tool is designed for OpenAPI v3 and above. It is not tested or expected to work with OpenAPI versions prior to v3.

    If you have an older specification, you must migrate it to OpenAPI 3.0+ before using openapi-zod-client. You can use the Swagger Editor to perform this conversion via the Edit -> Convert to OpenAPI 3.0 menu.

  6. Example: Transform OpenAPI 3.0 spec to Zodios client

    main

    The following example demonstrates how openapi-zod-client transforms a standard OpenAPI 3.0 YAML specification into a fully typed Zodios client.

    1. Input: An OpenAPI 3.0 YAML file defining paths (e.g., /pets, /pets/{petId}), parameters, and schemas (e.g., Pet, Error).
    2. Output: A TypeScript file containing:
      • zod schemas for all components.
      • An endpoints definition created via makeApi.
      • A Zodios instance representing the API.
      • A helper function createApiClient to instantiate the client with a base URL.
    import { makeApi, Zodios } from "@zodios/core";
    import { z } from "zod";
    
    const Pet = z.object({ id: z.number().int(), name: z.string(), tag: z.string().optional() });
    const Pets = z.array(Pet);
    const Error = z.object({ code: z.number().int(), message: z.string() });
    
    export const schemas = {
        Pet,
        Pets,
        Error,
    };
    
    const endpoints = makeApi([
        {
            method: "get",
            path: "/pets",
            requestFormat: "json",
            parameters: [
                {
                    name: "limit",
                    type: "Query",
                    schema: z.number().int().optional(),
                },
            ],
            response: z.array(Pet),
        },
        {
            method: "post",
            path: "/pets",
            requestFormat: "json",
            response: z.void(),
        },
        {
            method: "get",
            path: "/pets/:petId",
            requestFormat: "json",
            parameters: [
                {
                    name: "petId",
                    type: "Path",
                    schema: z.string(),
                },
            ],
            response: Pet,
        },
    ]);
    
    export const api = new Zodios(endpoints);
    
    export function createApiClient(baseUrl: string) {
        return new Zodios(baseUrl, endpoints);
    }
  7. Configure Zodios endpoint generation via TemplateContext options

    main

    When using the internal generation logic (or extending the client), you can influence how Zodios endpoints are extracted from an OpenAPI document using the options object (of type TemplateContext["options"]).

    Key configuration options include:

    • isMainResponseStatus: Determines which HTTP status code is treated as the primary successful response. Can be a specific status string (e.g., '200') or a function (status: number) => boolean.
    • isErrorStatus: Determines which status codes are treated as errors. Defaults to non-2xx codes if nullish.
    • isMediaTypeAllowed: Filters which media types are processed. Defaults to application/json if nullish.
    • withAlias: If true, uses the operationId from the spec as the endpoint alias; otherwise, it generates one from the path and method.
    • withDeprecatedEndpoints: If true, includes endpoints marked as deprecated in the OpenAPI spec.
    • withDescription: If true, attempts to attach descriptions from the OpenAPI parameters to the generated Zod schemas.
    • withAllResponses: If true, includes an array of all responses in the endpoint definition.
    • exportAllNamedSchemas: If true, ensures all named schemas are exported as individual variables.
    • complexityThreshold: Controls when a Zod schema is assigned to a variable versus being inlined. A value of -1 inlines everything. The default is 4.
    • defaultStatusBehavior: Controls how the default response in OpenAPI is handled. Options are 'spec-compliant' (default) or 'auto-correct' (promotes default to the main response if no other main response is found).
    • endpointDefinitionRefiner: A callback function (endpointDefinition, operation) => endpointDefinition that allows you to manually inject or modify fields in the generated endpoint definition before it is passed to the template.
  8. Configure TypeScript conversion options

    main

    When calling getTypescriptFromOpenApi, you can pass an options object (of type TemplateContext["options"]) to control the output format:

    • allReadonly: (boolean) If true, wraps generated types (like arrays or objects) with the readonly modifier.
    • withDocs: (boolean) If true, generates and attaches JSDoc comments to the resulting TypeScript nodes based on the OpenAPI schema descriptions.
  9. Configure the openapi-zod-client CLI

    main

    The CLI provides several options to customize the generated Zodios client. Use these flags to control output structure, validation behavior, and documentation.

    Common Options

    • -o, --output <path>: Output path for the generated .ts file (defaults to <input>.client.ts).
    • -t, --template <path>: Path to a Handlebars template for custom generation.
    • -p, --prettier <path>: Path to a Prettier config file for formatting the output.
    • -b, --base-url <url>: Sets the base URL for the API client.
    • --export-schemas: Exports all #/components/schemas.
    • --export-types: Defines types for all object schemas in #/components/schemas.
    • --with-description: Adds z.describe(xxx) to generated schemas.
    • --with-docs: Adds JSDoc comments to generated types.
    • --group-strategy <strategy>: Groups endpoints by none, tag, method, tag-file, or method-file.
    • --strict-objects: Enables strict validation for objects (disallows unknown keys). Defaults to false.
  10. Convert OpenAPI schemas to TypeScript with getTypescriptFromOpenApi

    main

    The getTypescriptFromOpenApi function is the core engine for converting OpenAPI SchemaObject or ReferenceObject definitions into TypeScript AST nodes, TypeDefinitionObjects, or strings. It handles complex OpenAPI features including $ref resolution, oneOf, anyOf, allOf, enum, nullable, and additionalProperties.

    Key Features

    • Reference Resolution: Uses a TsConversionContext and a DocumentResolver to resolve and track $ref links, preventing infinite loops in circular schemas.
    • Composition Support: Correctly maps allOf to intersections, oneOf/anyOf to unions, and handles nullable by adding null to unions.
    • Object Mapping: Converts OpenAPI objects to TypeScript type literals or type aliases. It supports additionalProperties by creating index signatures.
    • Readonly Option: If options.allReadonly is set, it wraps generated types in readonly modifiers.
    • JSDoc Generation: If options.withDocs is enabled, it attaches JSDoc comments derived from the schema to the resulting TypeScript nodes.

    Arguments

    • schema: The OpenAPI SchemaObject or ReferenceObject to convert.
    • meta: Metadata for the conversion, such as name (required for creating named type aliases) or $ref.
    • ctx: A TsConversionContext containing the resolver and nodeByRef map, essential for handling references and circularity.
    • options: Configuration for the conversion process (e.g., allReadonly, withDocs).
    import { getTypescriptFromOpenApi } from './openApiToTypescript';
    
    // Example usage (conceptual):
    const tsNode = getTypescriptFromOpenApi({
        schema: openApiSchema,
        meta: { name: 'User' },
        ctx: conversionContext,
        options: { allReadonly: true, withDocs: true }
    });
  11. Generate a Zod client from OpenAPI with generateZodClientFromOpenAPI

    main

    Use generateZodClientFromOpenAPI to programmatically generate a Zod-based client from an OpenAPI specification. This is the primary entry point for generating clients within your own tools or scripts.

    import { generateZodClientFromOpenAPI } from 'openapi-zod-client';
    
    // Example usage (conceptual):
    // await generateZodClientFromOpenAPI({
    //   input: 'path/to/openapi.yaml',
    //   output: 'path/to/client.ts',
    //   // ... other options
    // });