OrvalOrval

Client with Zod

Combine Zod validation with HTTP clients

Use Zod schemas alongside your SWR or TanStack Query client for runtime validation.

Configuration

Generate both the HTTP client and Zod schemas:

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

export default defineConfig({
  // HTTP client generation
  petstore: {
    input: {
      target: './petstore.yaml',
    },
    output: {
      mode: 'tags-split',
      client: 'swr',
      target: 'src/api/endpoints',
      schemas: 'src/api/models',
      mock: true,
    },
  },
  // Zod schema generation
  petstoreZod: {
    input: {
      target: './petstore.yaml',
    },
    output: {
      mode: 'tags-split',
      client: 'zod',
      target: 'src/api/endpoints',
      fileExtension: '.zod.ts',
    },
  },
});

Use fileExtension: '.zod.ts' to avoid filename conflicts with the HTTP client files.

Generated Files

src/api/
├── endpoints/
│   └── pets/
│       ├── pets.ts       # SWR hooks
│       ├── pets.msw.ts   # MSW mocks
│       ├── pets.faker.ts # Faker mocks
│       └── pets.zod.ts   # Zod schemas
└── models/
    └── ...

Usage

import { useListPets, useCreatePets } from './api/endpoints/pets/pets';
import { CreatePetsBodyItem } from './api/endpoints/pets/pets.zod';

function App() {
  const { data } = useListPets();
  const { trigger } = useCreatePets();

  const createPet = async () => {
    const pet = { name: 'Buddy', tag: 'dog' };

    try {
      // Validate before sending
      const validatedPet = CreatePetsBodyItem.parse(pet);
      await trigger([validatedPet]);
    } catch (error) {
      if (error instanceof ZodError) {
        console.error('Validation failed:', error.errors);
      }
    }
  };

  return (
    <div>
      <button onClick={createPet}>Create Pet</button>
      {data?.map((pet) => (
        <div key={pet.id}>{pet.name}</div>
      ))}
    </div>
  );
}

Full Example

See the complete SWR with Zod example on GitHub.

On this page