Skip to main content
The Zod plugin generates Zod schemas from your OpenAPI specification. Zod is a TypeScript-first schema validation library with static type inference, zero dependencies, and excellent developer experience.

Installation

Install the required dependencies:
npm install zod @hey-api/openapi-ts

Configuration

Add the Zod plugin to your openapi-ts.config.ts:
openapi-ts.config.ts
import { defineConfig } from '@hey-api/openapi-ts';

export default defineConfig({
  input: 'path/to/openapi.json',
  output: {
    path: './src/client',
  },
  plugins: [
    '@hey-api/typescript',
    {
      name: '@hey-api/zod',
      // Configuration options
    },
  ],
});

Compatibility Versions

The Zod plugin supports multiple Zod versions through the compatibilityVersion option:
Generates schemas compatible with Zod 4:
{
  name: '@hey-api/zod',
  compatibilityVersion: 4,
}
The plugin auto-detects your installed Zod version and uses version 4 by default if Zod 4.x is installed.
The plugin automatically detects your installed Zod version and warns you if there’s a mismatch with your configured compatibilityVersion.

Schema Generation

Definitions

Generate schemas for reusable component definitions:
{
  name: '@hey-api/zod',
  definitions: true,  // Enable definitions (default)
}
Customize definition schema names and casing:
{
  name: '@hey-api/zod',
  definitions: {
    enabled: true,
    name: 'z{{name}}',  // Naming pattern (default)
    case: 'camelCase',  // Identifier casing
  },
}
Generated output:
zod.gen.ts
import { z } from 'zod';

/**
 * A pet in the pet store
 */
export const zPet = z.object({
  id: z.number().int(),
  name: z.string(),
  status: z.enum(['available', 'pending', 'sold']).optional(),
});

Requests

Generate validators for API request data (body, query, path, headers):
{
  name: '@hey-api/zod',
  requests: {
    enabled: true,
    name: 'z{{name}}Data',  // Naming pattern
    case: 'camelCase',
  },
}
Generated output:
zod.gen.ts
export const zGetPetByIdData = z.object({
  path: z.object({
    petId: z.number().int(),
  }),
  query: z.object({
    includeDetails: z.boolean().optional(),
  }).optional(),
});

Responses

Generate validators for API responses:
{
  name: '@hey-api/zod',
  responses: {
    enabled: true,
    name: 'z{{name}}Response',  // Naming pattern
    case: 'camelCase',
  },
}
Generated output:
zod.gen.ts
export const zGetPetByIdResponse = z.object({
  id: z.number().int(),
  name: z.string(),
  status: z.enum(['available', 'pending', 'sold']).optional(),
});

Webhooks

Generate validators for webhook payloads:
{
  name: '@hey-api/zod',
  webhooks: {
    enabled: true,
    name: 'z{{name}}WebhookRequest',
    case: 'camelCase',
  },
}

Type Inference

Generate TypeScript types from Zod schemas:
{
  name: '@hey-api/zod',
  types: {
    infer: true,  // Generate inferred types
  },
}
Configure type generation per schema category:
{
  name: '@hey-api/zod',
  definitions: {
    types: {
      infer: {
        enabled: true,
        name: '{{name}}ZodType',  // Naming pattern
        case: 'PascalCase',       // Type name casing
      },
    },
  },
  requests: {
    types: {
      infer: {
        enabled: true,
        name: '{{name}}DataZodType',
      },
    },
  },
}
Generated output:
zod.gen.ts
import { z } from 'zod';

export const zPet = z.object({
  id: z.number().int(),
  name: z.string(),
});

// Inferred TypeScript type
export type PetZodType = z.infer<typeof zPet>;

Date Handling

Configure how date and datetime values are validated:
{
  name: '@hey-api/zod',
  dates: {
    local: false,   // Allow unqualified datetimes (default: false)
    offset: false,  // Include timezone offset (default: false)
  },
}
{
  dates: {
    local: false,
    offset: false,
  },
}
Generates: z.iso().datetime()Validates: 2024-01-15T12:30:00Z

Metadata Support

Enable Zod metadata for additional schema information:
{
  name: '@hey-api/zod',
  metadata: true,  // Enable metadata (default: false)
}
Metadata is useful for documentation, code generation, AI structured outputs, and form validation.

Usage Examples

Validating API Requests

import { zCreatePetData } from './client/zod.gen';

// Validate request data
const result = await zCreatePetData.safeParseAsync({
  body: {
    name: 'Fluffy',
    status: 'available',
  },
});

if (result.success) {
  // Data is valid and typed
  await fetch('/api/pets', {
    method: 'POST',
    body: JSON.stringify(result.data.body),
  });
} else {
  // Handle validation errors
  console.error(result.error.errors);
}

Validating API Responses

import { zGetPetByIdResponse } from './client/zod.gen';

const response = await fetch('/api/pets/123');
const data = await response.json();

// Validate and parse response
const pet = await zGetPetByIdResponse.parseAsync(data);
// pet is now fully typed: { id: number; name: string; status?: string }

Type Inference

import { z } from 'zod';
import { zPet } from './client/zod.gen';

// Infer TypeScript type from Zod schema
type Pet = z.infer<typeof zPet>;

const pet: Pet = {
  id: 1,
  name: 'Fluffy',
};

Form Validation with React Hook Form

import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { zCreatePetData } from './client/zod.gen';

// Extract the body schema from the request data schema
const CreatePetSchema = zCreatePetData.shape.body;
type CreatePetForm = z.infer<typeof CreatePetSchema>;

function CreatePetForm() {
  const { register, handleSubmit, formState: { errors } } = useForm<CreatePetForm>({
    resolver: zodResolver(CreatePetSchema),
  });

  const onSubmit = async (data: CreatePetForm) => {
    await fetch('/api/pets', {
      method: 'POST',
      body: JSON.stringify(data),
    });
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register('name')} />
      {errors.name && <span>{errors.name.message}</span>}

      <select {...register('status')}>
        <option value="available">Available</option>
        <option value="pending">Pending</option>
        <option value="sold">Sold</option>
      </select>

      <button type="submit">Create Pet</button>
    </form>
  );
}

Advanced Configuration

Custom Naming Patterns

Use functions for dynamic naming:
{
  name: '@hey-api/zod',
  definitions: {
    name: (name: string) => `zodSchema${name}`,
  },
  requests: {
    name: (name: string) => `${name}RequestSchema`,
  },
}

Selective Schema Generation

Disable specific schema categories:
{
  name: '@hey-api/zod',
  definitions: true,
  requests: false,    // Don't generate request schemas
  responses: true,
  webhooks: false,    // Don't generate webhook schemas
}

Global Configuration

import { defineConfig } from '@hey-api/openapi-ts';

export default defineConfig({
  input: 'path/to/openapi.json',
  output: {
    path: './src/client',
  },
  plugins: [
    '@hey-api/typescript',
    {
      name: '@hey-api/zod',
      compatibilityVersion: 4,
      case: 'camelCase',
      comments: true,
      metadata: false,
      dates: {
        offset: true,
      },
      definitions: {
        name: 'z{{name}}',
        types: {
          infer: true,
        },
      },
      requests: {
        name: 'z{{name}}Data',
        types: {
          infer: true,
        },
      },
      responses: {
        name: 'z{{name}}Response',
        types: {
          infer: true,
        },
      },
    },
  ],
});

Type Reference

The Zod plugin exports these configuration types:
import type { ZodPlugin } from '@hey-api/zod';
import { defineConfig } from '@hey-api/zod';

// Use the type helper for better type safety
const zodConfig = defineConfig({
  name: '@hey-api/zod',
  // ... your configuration
});

Validators Overview

Learn about validator plugins and their benefits

Valibot Plugin

Alternative validator with smaller bundle size

Zod Documentation

Official Zod library documentation

Build docs developers (and LLMs) love