OrvalOrval

Client with Effect

Combine Effect validation with HTTP clients

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

Configuration

Generate both the HTTP client and Effect 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,
    },
  },
  // Effect schema generation
  petstoreEffect: {
    input: {
      target: './petstore.yaml',
    },
    output: {
      mode: 'tags-split',
      client: 'effect',
      target: 'src/api/endpoints',
      fileExtension: '.effect.ts',
    },
  },
});

Use fileExtension: '.effect.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.effect.ts   # Effect schemas
└── models/
    └── ...

Usage

import { Schema as S } from 'effect';

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

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

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

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

  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 Effect example on GitHub.

On this page