Zod Showcase

Ecosystem

Tools that work alongside Zod: schema generators that stay in sync with your API specs, and monitoring integrations that make validation failures easier to debug in production.

Code Generation

Generate Zod schemas from an OpenAPI spec so they stay in sync with the API contract. No manual schema writing needed.

Orval ↗

Generates fully-typed API clients (Axios, Fetch, React Query, and more) from an OpenAPI spec. Setting the client to "zod" outputs Zod schemas instead of plain TypeScript interfaces.

Problem it solves

Maintaining handwritten Zod schemas for a large API is tedious, and they drift from the spec whenever the API changes. Orval re-generates them from the OpenAPI spec on demand.

orval.config.ts
import { defineConfig } from 'orval';

export default defineConfig({
  petstore: {
    input: './openapi.yaml',
    output: {
      target: './src/api/petstore.ts',
      // 'zod' mode emits Zod schemas instead of TS interfaces
      client: 'zod',
    },
  },
});
Generated output
// src/api/petstore.ts (generated — do not edit)
import { z } from 'zod';

export const petSchema = z.object({
  id: z.number().int(),
  name: z.string(),
  status: z.enum(['available', 'pending', 'sold']).optional(),
});

export type Pet = z.infer<typeof petSchema>;

openapi-zod-client ↗

A lightweight CLI that reads an OpenAPI spec and outputs Zod schemas plus a typed HTTP client using Zodios. No config file needed.

Problem it solves

When you want Zod schemas and a typed endpoint client without a heavy generator framework. A single command takes your spec in and spits a ready-to-use client out.

CLI
npx openapi-zod-client ./openapi.yaml -o ./src/api/client.ts
Generated output
// src/api/client.ts (generated — do not edit)
import { makeApi, Zodios } from '@zodios/core';
import { z } from 'zod';

const Pet = z.object({
  id: z.number().int().optional(),
  name: z.string(),
  status: z.enum(['available', 'pending', 'sold']).optional(),
});

const api = makeApi([
  {
    method: 'get',
    path: '/pets/:id',
    alias: 'getPetById',
    response: Pet,
  },
]);

export const client = new Zodios('/api', api);

Hey API / openapi-ts ↗

A plugin-based OpenAPI TypeScript codegen tool. The Zod plugin generates Zod schemas alongside TypeScript types and service clients, all from a single config file.

Problem it solves

When you need a full-featured, extensible codegen pipeline that can grow with your project. Plugins let you opt into exactly what you need: types, services, Zod schemas, or all three.

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

export default defineConfig({
  input: './openapi.yaml',
  output: './src/client',
  plugins: [
    '@hey-api/typescript',
    {
      name: '@hey-api/zod',
      output: 'schemas',
    },
  ],
});
Generated output
// src/client/schemas.ts (generated — do not edit)
import { z } from 'zod';

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

Observability

Get structured context when validation fails in production, not just a generic error message.

Sentry — zodErrorsIntegration ↗

Enhances Sentry error reporting for apps using Zod. When a ZodError is captured, the integration attaches the full list of validation issues as structured data on the Sentry event: field paths, error codes, and the values that were received.

Problem it solves

Without this integration, Sentry shows only the top-level ZodError message, which tells you validation failed but not where or why. With it, every failed parse in production is fully debuggable: you see exactly which fields failed, what was expected, and what was received.

Sentry init
import * as Sentry from '@sentry/browser'; // or @sentry/node

Sentry.init({
  dsn: 'YOUR_DSN',
  integrations: [
    Sentry.zodErrorsIntegration({
      // Max number of Zod issues inlined per event (default: 10)
      limit: 10,
      // Save the full issue list as a JSON attachment (default: false)
      saveZodIssuesAsAttachment: false,
    }),
  ],
});
Error context in Sentry
[
  {
    "code": "too_small",
    "path": ["name"],
    "message": "Name is required",
    "minimum": 1,
    "type": "string",
    "inclusive": true,
    "received": ""
  },
  {
    "code": "invalid_string",
    "path": ["email"],
    "message": "Invalid email format",
    "validation": "email",
    "received": "not-an-email"
  }
]