OrvalOrval

Output

Output configuration options

target

Type: String

Output path for generated files.

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

export default defineConfig({
  petstore: {
    output: {
      target: 'src/petstore.ts',
    },
  },
});

client

Type: String | Function Default: 'axios-functions' Options: angular, angular-query, axios, axios-functions, react-query, solid-start, solid-query, svelte-query, vue-query, swr, zod, effect, hono, fetch, mcp

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      client: 'react-query',
    },
  },
});

You can also provide a function to create a custom client generator.

axios client

The axios client generates a factory function with an optional axios instance parameter for dependency injection:

import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';

export const getPetsApi = (axiosInstance: AxiosInstance = axios) => ({
  listPets: (params?: ListPetsParams) =>
    axiosInstance.get<Pet[]>('/pets', { params }),
});

You can inject your own axios instance for testing or custom configuration:

const customAxios = axios.create({ baseURL: 'https://api.example.com' });
const api = getPetsApi(customAxios);

httpClient

Type: 'fetch' | 'axios' | 'angular' Default: 'fetch'

HTTP transport used by compatible generated clients. Use fetch or axios for query clients, and angular for angular and angular-query output.

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      client: 'swr',
      httpClient: 'axios',
    },
  },
});

schemas

Type: String | Object | false Default: Same as target

Output path for generated model types. Set to false to disable separate schema file output.

String form

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      schemas: './api/model',
    },
  },
});

Object form

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      schemas: {
        path: './api/model',
        type: 'typescript', // 'typescript' | 'zod'
      },
    },
  },
});
PropertyTypeDescription
pathstringFilesystem path for schema output
typestring'typescript' (default) or 'zod' — optional
importPathstringOptional package import specifier (see below)
splitByTagsbooleanOrganize schemas into per-tag subdirectories (default false, see below)

importPath

When importPath is set, generated client files import schema types from that package specifier instead of computing a relative filesystem path:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      target: './libs/client/angular/src/lib/endpoints',
      schemas: {
        path: './libs/client/models/src/lib',
        type: 'typescript',
        importPath: '@acme/client/models',
      },
    },
  },
});
// Without importPath — computed relative path:
import type { Pet } from '../models/pet';

// With importPath: '@acme/client/models':
import type { Pet } from '@acme/client/models';

Schemas are still written to the filesystem path — only the generated import statements change.

Requirements when using importPath:

  • The target package must export the types at the specified import path.
  • With indexFiles: true (recommended), all types are imported from the single importPath (e.g., @acme/models).
  • With indexFiles: false, each schema is imported individually (e.g., @acme/models/pet). The package must support these subpath exports. For Zod schemas (type: 'zod') the per-file suffix is .zod, so the package must also expose ./pet.zod (e.g., @acme/models/pet.zod).
  • If using faker schema factories (mock: { generators: [{ type: 'faker', schemas: true }] }), the package must also export ./index.faker. When the package can't expose a sub-path (e.g. importPath resolves to a single barrel file via tsconfig path mappings), set schemasImportPath on the faker generator to point faker factories at a separate import path.
  • If using factory methods (factoryMethods), each schema is imported individually regardless of indexFiles.
  • When importPath is set, the relative-path computation in factoryMethods.outputDirectory is bypassed: factories resolve imports against the package specifier rather than the on-disk factory output directory.
  • Config normalization rejects invalid importPath values (empty, whitespace, relative, or absolute paths) before generation runs — see Validation of importPath.

Validation of importPath

During config normalization, orval rejects the following importPath values with a clear error message before generation runs:

  • Empty string.
  • Strings that are empty after trimming whitespace (e.g. " "), or that contain leading/trailing whitespace around a valid-looking specifier.
  • Relative specifiers starting with ./ or ../ (e.g. ./models, ../models).
  • Absolute paths — POSIX (starting with /, e.g. /abs/models) or Windows (drive-letter like C:\models, or UNC like \\server\share\models).

splitByTags

When splitByTags is true, schemas are organized into per-tag subdirectories instead of a single flat directory. Schemas referenced by only one tag go in that tag's directory; schemas referenced by multiple tags (or not referenced by any operation) remain at the root of the schema directory. Works with any mode (single, split, tags, tags-split).

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      mode: 'tags-split',
      schemas: {
        path: './api/model',
        splitByTags: true,
      },
    },
  },
});

Result:

api/model/
├── error.ts               ← schemas used by 2+ tags (or unreferenced)
├── pagination.ts
├── pets/                  ← schemas only used by "pets" operations
│   ├── pet.ts
│   ├── createPetsBody.ts
│   ├── listPetsParams.ts
│   └── index.ts
└── index.ts               ← root barrel re-exporting shared files + tag dirs

Cross-tag imports from within a tag subdirectory resolve to the parent:

// pets/pet.ts
import type { Error } from '../error';

Requirements:

  • Works with any mode (single, split, tags, tags-split).
  • Incompatible with operationSchemas — operation-derived types are placed within their tag directories automatically.
  • Schema-to-tag mapping is transitive: if Pet imports Dog which imports Dachshund, all three land in the same directory.

operationSchemas

Type: String

Separate path for operation-derived types (params, bodies, responses).

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      schemas: './api/model',
      operationSchemas: './api/model/params',
    },
  },
});

fileExtension

Type: String Default: .ts

Customize file extension for generated files:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      mode: 'split',
      target: './gen/endpoints',
      schemas: './gen/model',
      fileExtension: '.gen.ts',
    },
  },
});

Result:

src/gen/
├── endpoints
│   └── swaggerPetstore.gen.ts
└── model
    ├── listPetsParams.ts
    └── pets.ts

schemaFileExtension

Type: String Default: '.zod.ts' when generating Zod schemas (schemas: { type: 'zod' } or client: 'zod' + generateReusableSchemas), otherwise the same as fileExtension.

Override the file extension for schema artifacts only — without affecting the global fileExtension (which still drives client output, mock files, etc.). Useful when you want client files at one extension and schema files at another:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      mode: 'split',
      client: 'zod',
      target: './gen/endpoints',
      schemas: './gen/model',
      fileExtension: '.ts',
      // Keep client files at .ts but emit reusable Zod schemas as .zod.ts:
      schemaFileExtension: '.zod.ts',
      override: { zod: { generateReusableSchemas: true } },
    },
  },
});

namingConvention

Type: 'camelCase' | 'PascalCase' | 'snake_case' | 'kebab-case' Default: 'camelCase'

Naming convention for generated files:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      namingConvention: 'PascalCase',
      mode: 'split',
      target: './gen/endpoints',
    },
  },
});

workspace

Type: String

Base folder for all generated files. Creates an index.ts with exports:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      workspace: 'src/',
      target: './petstore.ts',
    },
  },
});

mode

Type: 'single' | 'split' | 'tags' | 'tags-split' Default: 'single'

single

Everything in one file.

split

Separate files for implementation, schemas, and mocks:

my-app/src/
├── petstore.schemas.ts
├── petstore.msw.ts
└── petstore.ts

tags

One file per OpenAPI tag:

my-app/src/
├── pets.ts
└── petstore.schemas.ts

tags-split

Folder per tag with split files:

my-app/src/
├── petstore.schemas.ts
└── pets/
    ├── petstore.msw.ts
    └── petstore.ts

When schemas is configured, models go to a dedicated directory with per-file output. With indexFiles enabled (the default), a barrel index.ts re-exports all schemas so service files import from a single path. With indexFiles: false, each schema is imported individually (e.g., ../models/pet). All schemas — domain types, generic wrappers, and bound aliases — live in this single shared directory and are never duplicated per tag:

my-app/src/
├── models/
│   ├── index.ts
│   ├── pet.ts
│   ├── listResponse.ts
│   ├── pagination.ts
│   └── userListResponse.ts
├── pets/
│   └── pets.ts              ← imports from ../models
└── users/
    └── users.ts             ← imports from ../models

To organize schemas into per-tag subdirectories instead of a flat directory, use splitByTags:

my-app/src/
├── models/
│   ├── pagination.ts          ← shared schemas at root
│   ├── error.ts
│   ├── pets/
│   │   ├── pet.ts
│   │   ├── listPetsParams.ts
│   │   └── index.ts
│   ├── users/
│   │   ├── user.ts
│   │   ├── listUsersParams.ts
│   │   └── index.ts
│   └── index.ts               ← root barrel
├── pets/
│   └── pets.ts
└── users/
    └── users.ts

baseUrl

Type: String | Object Default: ''

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      baseUrl: 'https://api.example.com',
    },
  },
});

For the angular client, prefer override.angular.baseUrl when you need the base URL resolved through Angular's dependency injection (for example, per-API gateway routing or TestBed overrides) instead of baked into every generated route string. baseUrl and override.angular.baseUrl are mutually exclusive on the same output.

runtime

Type: String

Embed a JavaScript expression into generated request URLs so the same build can call different hosts at runtime (for example with Docker images and environment variables). The value is emitted inside template literals in generated clients; only use trusted expressions from your configuration. JavaScript expression used inside generated template literals for the request base URL. Set this to the expression only (for example process.env.API_BASE_URL), not including `${...}`; Orval wraps it for you.

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      baseUrl: {
        runtime: 'process.env.API_BASE_URL',
      },
    },
  },
});

imports

Type: GeneratorImport[]

Optional. When runtime references a symbol from another module, list the imports Orval should emit into generated clients. Paths are relative to the generated file, same idea as mutator imports. The runtime expression must be valid where the generated code runs (after those imports).

Use a default import:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      baseUrl: {
        runtime: 'apiBase',
        imports: [{ name: 'apiBase', importPath: '../config/api' }],
      },
    },
  },
});

Or a named export used as an object (for example import { env } from '../../env' and env.API_BASE_URL in application code) — set runtime to that property access and import the object under name:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      baseUrl: {
        runtime: 'env.API_BASE_URL',
        imports: [{ name: 'env', importPath: '../../env' }],
      },
    },
  },
});

Adjust importPath so it resolves from the generated client file to your module (the example assumes the client is nested deeper than env.ts).

getBaseUrlFromSpecification

Type: Boolean

Read the base URL from the OpenAPI servers field instead of a fixed string. When true, Orval resolves it from the spec’s servers entry (optionally with variables and index below).

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      baseUrl: {
        getBaseUrlFromSpecification: true,
        variables: {
          environment: 'api.dev',
        },
      },
    },
  },
});

variables

Type: Record<string, string>

Values for variables used in server URL templates from the OpenAPI servers field.

index

Type: Number

Which servers entry to use (0-based) when multiple URLs are defined:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      baseUrl: {
        getBaseUrlFromSpecification: true,
        index: 1, // Use second server URL
      },
    },
  },
});

mock

Type: Boolean | Object | Function Default: false

Configures one or more mock generators. The shorthand mock: true enables both MSW and Faker mock files with default options:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      mock: true,
    },
  },
});

Each entry in mock.generators produces its own file (<filename>.msw.ts, <filename>.faker.ts, ...). Set mock: false (or omit it) to disable mock generation entirely.

Mocks Options

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      mock: {
        indexMockFiles: true,
        generators: [
          {
            type: 'msw',
            delay: 1000,
            useExamples: false,
            generateEachHttpStatus: false,
            baseUrl: '/api',
            locale: 'en',
          },
          {
            type: 'faker',
            useExamples: false,
          },
        ],
      },
    },
  },
});
OptionTypeDefaultDescription
indexMockFilesBooleanfalseIn split and tags-split modes, emit one root-level index.<ext>.ts file per generator entry that re-exports the mocks (e.g. index.msw.ts, index.faker.ts). In tags-split it re-exports the per-tag mocks; in split it re-exports the single mock file. Useful to keep mocks (e.g. MSW) in a dedicated barrel that production/model barrels never import.
pathStringundefinedShared output directory for all mock files. Per-generator path values override this. When set in single or tags mode, mock code is written to separate files (relative to path) instead of being inlined into the implementation file. Ignored on function-form generators, which always fall back to the shared path.
generatorsArray<MockOptions | Function>[]One entry per output mock file. Each entry can be an object (MockOptions) or a custom ClientMockBuilder function.
orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      mock: {
        path: './src/api/mocks',
        generators: [
          { type: 'msw', path: './src/api/mocks/msw' },
          { type: 'faker' },
        ],
      },
    },
  },
});

MSW generator (type: 'msw')

OptionTypeDefaultDescription
type'msw'requiredDiscriminator for MSW handler generation.
pathStringundefinedOutput directory for this generator's mock files. Overrides the shared mock.path when set. When provided in single or tags mode, mock code is written to separate files (relative to path) instead of being inlined into the implementation file.
operationResponsesBooleantrueEmit get<Op>ResponseMock factories in the MSW output. Set to false to generate handlers only, response fallbacks become undefined. No effect when a Faker generator also emits the factories, the handlers then import them from the .faker file. Honored in split and tags-split modes.
delayNumber | Function | falsefalseResponse delay in ms.
delayFunctionLazyExecuteBooleanfalseExecute delay function at runtime instead of at build time.
baseUrlString''Base URL for the generated MSW handlers.
useExamplesBooleanfalseUse OpenAPI examples to seed response values.
generateEachHttpStatusBooleanfalseGenerate response factories for every documented status code.
localeString'en'Faker.js locale.
preferredContentTypeStringundefinedPreferred content type when an operation lists more than one.

Faker generator (type: 'faker')

The Faker generator emits the same get<Op>ResponseMock factories MSW would emit, but without any msw dependency or HTTP handler code. Useful for tests or stories that only need fake response data.

In split and tags-split modes, configuring Faker alongside MSW moves the get<Op>ResponseMock factories to the .faker file. The .msw file only contains the handlers and imports (and re-exports) the factories instead of duplicating them. If the Faker generator is configured with operationResponses: false it emits no factories, so there is nothing to move and the .msw file keeps them inline.

OptionTypeDefaultDescription
type'faker'requiredDiscriminator for Faker-only output.
pathStringundefinedOutput directory for this generator's mock files. Overrides the shared mock.path when set. When provided in single or tags mode, mock code is written to separate files (relative to path) instead of being inlined into the implementation file.
schemasBooleanfalseEmit a consolidated mock factory file (get<SchemaName>Mock) for every entry under components/schemas.
schemasImportPathStringundefinedPackage specifier for importing the schema-level faker factories emitted by schemas: true (e.g. @acme/models/fakers). When set, used verbatim instead of appending /index.faker to schemas.importPath — useful when the production barrel can't expose a sub-path export. Requires schemas: true and schemas.importPath. Only applies when schemas: true is set on the same generator.
operationResponsesBooleantrueEmit per-operation response mock factories (the historical behavior). Set to false together with schemas: true to get only the consolidated schema factories.
useExamplesBooleanfalseUse OpenAPI examples to seed response values.
generateEachHttpStatusBooleanfalseGenerate response factories for every documented status code.
localeString'en'Faker.js locale.
preferredContentTypeStringundefinedPreferred content type when an operation lists more than one.
arrayItemsBooleanfalseEmit reusable mock factories for object-like array item schemas in operation responses.

schemasImportPath

Only applies when schemas: true is set on the same faker generator (requires both schemas: true and schemas.importPath). When schemas.importPath resolves to a single barrel file (e.g. via tsconfig path mappings), appending /index.faker produces an unresolvable sub-path. schemasImportPath lets you point faker factories at a separate import path so you can expose them through a dedicated barrel:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      target: './libs/client/sdk/generated',
      schemas: {
        path: './libs/data-layer/sdk/generated',
        importPath: '@acme/data-layer/sdk',
      },
      mock: {
        path: './libs/client/sdk/mocks',
        generators: [
          {
            type: 'faker',
            schemas: true,
            schemasImportPath: '@acme/data-layer/sdk/fakers',
          },
        ],
      },
    },
  },
});
// Without schemasImportPath (default — joins importPath with /index.faker):
import { getPetMock } from '@acme/data-layer/sdk/index.faker'; // may not resolve

// With schemasImportPath: '@acme/data-layer/sdk/fakers':
import { getPetMock } from '@acme/data-layer/sdk/fakers';

indexFiles

Type: Boolean Default: true

Generate index.ts files for schemas.

tagsSplitDeduplication

Type: Boolean Default: false

In tags-split mode, extract shared infrastructure types (e.g. HTTPStatusCode* emitted by the fetch client) into a single common-types.ts file and generate a barrel index.ts at the target root.

When enabled alongside indexFiles: true:

  • Shared types that would otherwise be duplicated across per-tag files are collected and written once to [commonTypesFileName].ts
  • Each per-tag file imports shared types from the common file instead of declaring them inline
  • A barrel index.ts is generated with named re-exports for public shared types plus export * re-exports for each per-tag implementation file
orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      mode: 'tags-split',
      target: './src/api/endpoints.ts',
      schemas: './src/api/model',
      client: 'fetch',
      indexFiles: true,
      tagsSplitDeduplication: true,
    },
  },
});

Resulting structure:

src/api/
├── common-types.ts           ← shared types extracted once
├── index.ts                  ← barrel with named + wildcard re-exports
├── pets/
│   └── pets.ts               ← import type { ... } from '../common-types'
└── health/
    └── health.ts             ← import type { ... } from '../common-types'

When disabled (default), shared types are inlined per-tag and no barrel is generated — identical to previous behavior.

Suppressed when workspace is set (the workspace barrel handles aggregation).

commonTypesFileName

Type: String Default: 'common-types'

The file name (without extension) used for the shared types file when tagsSplitDeduplication is enabled.

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      mode: 'tags-split',
      indexFiles: true,
      tagsSplitDeduplication: true,
      commonTypesFileName: 'shared',  // generates shared.ts
    },
  },
});

docs

Type: Boolean | Object Default: false

Generate API docs using TypeDoc:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      docs: true,
      // or with config
      docs: {
        configPath: './typedoc.config.mjs',
      },
    },
  },
});

clean

Type: Boolean | String[] Default: false

Remove files left over from previous runs before regenerating. What is removed depends on whether Orval owns the directory.

target and schemas are wiped. Every file in them is removed (.d.ts files are preserved) — not only files produced by Orval, but also any other file that happens to live there. These are the directories Orval asks you to keep hand-written files out of, so it takes them as its own.

Configured mock directories are pruned. Set mock.path, or a mock generator's own path, to give mock files their own directory. You frequently keep hand-written code in that directory too — MSW's browser.ts and server.ts, fixtures, or a barrel. Orval removes only the files that it could have written there. The patterns are **/*.msw<ext> and **/*.faker<ext>. <ext> is your fileExtension, or one of the usual source extensions: .ts, .tsx, .mts, .cts, .js, .jsx, .mjs, .cjs. Orval matches those extensions as well as your own. Thus a change from .ts to .js does not strand the mock files of the earlier runs.

The same rule applies when the mock directory is inside target or schemas. The wipe does not go into a mock directory. Orval prunes that directory instead.

Orval keeps all the files that these patterns do not match. But it also removes every empty directory below each directory that it cleans. This includes your own empty directories.

Without a mock path there is no separate mock directory: mock output lands beside the implementation files and is covered by the target rule above.

Two limits apply to the prune patterns. A hand-written file with a name such as handlers.msw.ts or fixtures.faker.ts is removed, because Orval cannot tell it from its own output. And a compound fileExtension such as .gen.ts is matched only while you keep it configured. After you change it, remove the files of the earlier extension by hand.

When set to a String[], the array entries are extra glob patterns appended to the deletion list for the target and schemas directories. They are not applied to mock directories — a positive glob there could delete hand-written files Orval never produced. Use negated globs (prefixed with !) to preserve specific files from removal.

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      // preserve `important.ts` when wiping `target` / `schemas`
      clean: ['!**/important.ts'],
    },
  },
});

For example, to keep a committed swagger.json next to the generated output:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      target: './src/generated',
      clean: ['!**/swagger.json'],
    },
  },
});

clean removes the entire contents of target and schemas, not just generated files. Do not point either directly at a package or library entrypoint root that holds files you need to keep (package.json, ng-package.json, public-api.ts, etc.). Place generated output in a dedicated subdirectory such as ./generated/ so those files are never touched.

Keep hand-written files (mutators, transformers, app code) outside the target and schemas directories for the same reason. You can share a configured mock directory with hand-written code, because Orval prunes that directory and does not wipe it. This stays true when the mock directory is inside target or schemas. But keep to the two limits above: do not give a hand-written file a mock file name, and do not rely on Orval to keep your empty directories.

A directory configured by more than one project is cleaned by each of them before that project writes, so the last project to run wins and the earlier project's output is gone. This applies to target, schemas, and mock directories alike. Give each project its own output directories.

formatter

Type: 'prettier' | 'biome' | 'oxfmt' | undefined Default: undefined

Format generated files with the specified formatter. Only one formatter can be used at a time.

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      formatter: 'prettier',
    },
  },
});

headers

Type: Boolean Default: false

Generate typed parameters for the HTTP request headers an operation declares in the specification. When disabled, header parameters are omitted from the generated function signatures.

This is unrelated to override.header, which controls the comment block written at the top of each generated file.

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      headers: true,
    },
  },
});

tsconfig

Type: String | Object

Custom TypeScript configuration path or inline config:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      tsconfig: './tsconfig.json',
      // or inline
      tsconfig: {
        compilerOptions: {
          target: 'ES2020',
        },
      },
    },
  },
});

packageJson

Type: String

Path to your package.json (usually auto-detected).


override

transformer

Type: String | Function

Transform the generated output:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        transformer: 'src/yourfunction.js',
      },
    },
  },
});

mutator

Type: String | Object

Custom HTTP client implementation:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        mutator: {
          path: './api/mutator/custom-instance.ts',
          name: 'customInstance',
        },
      },
    },
  },
});

Example implementation:

custom-instance.ts
import Axios, { AxiosRequestConfig } from 'axios';

export const AXIOS_INSTANCE = Axios.create({ baseURL: '' });

export const customInstance = <T>(config: AxiosRequestConfig): Promise<T> => {
  return AXIOS_INSTANCE({ ...config }).then(({ data }) => data);
};

export type ErrorType<Error> = AxiosError<Error>;
export type BodyType<BodyData> = BodyData;

title

Type: String | Function

Customize the API service title (only for axios and angular clients):

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        title: (title) => `${title}Api`,
      },
    },
  },
});

namingConvention (property keys)

Type: Object

Change naming convention for property keys (not files):

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        namingConvention: {
          enum: 'PascalCase', // camelCase, PascalCase, snake_case, kebab-case
        },
      },
    },
  },
});

Type: Boolean | Function Default: the built-in header function

Customize or disable the comment block written at the top of each generated file. This is unrelated to output.headers, which controls HTTP request header parameters.

Pass false to omit the comment block, or a function to replace it. Passing true produces the same output as omitting the option, since any value that is neither false nor a function falls back to the built-in header.

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        header: (info) => [
          `Generated by Orval`,
          `Do not edit manually.`,
          ...(info.title ? [info.title] : []),
        ],
      },
    },
  },
});

override.query

TanStack Query options:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        query: {
          useQuery: true,
          useSuspenseQuery: true,
          useMutation: true,
          useInfinite: true,
          useSuspenseInfiniteQuery: true,
          useInfiniteQueryParam: 'nextId',
          usePrefetch: true,
          useInvalidate: true,
          useSetQueryData: true,
          useGetQueryData: true,
          signal: true,
          runtimeValidation: true,
          options: {
            staleTime: 10000,
          },
        },
      },
    },
  },
});

useQuery

Type: Boolean

Default: true for GET operations; false otherwise.

Generate useQuery hooks. When set explicitly, applies to all operations regardless of HTTP verb — setting useQuery: true routes POST, PUT, PATCH, and DELETE operations to useQuery hooks as well. This is useful for APIs that use POST for read-style endpoints (e.g. complex search bodies, GraphQL-style single-endpoint APIs).

Cache keys for non-GET operations are automatically namespaced by HTTP verb to avoid collisions with GET operations on the same path (e.g. ['POST', '/pets', body]).

Set to false to suppress useQuery hook generation; pair with useMutation: true (default for non-GET) if you want the request to be wired up as a Mutation instead.

useSuspenseQuery

Type: Boolean

Default: unset — opt-in.

Generate useSuspenseQuery hooks. When set globally, this only applies to GET operations; per-operation overrides (override.operations.<id>.query.useSuspenseQuery) bypass that restriction for individual operations.

useMutation

Type: Boolean

Default: true for non-GET operations; false otherwise.

Generate useMutation hooks. When set explicitly, applies to all operations regardless of HTTP verb — setting useMutation: true (globally or via override.operations.<id>.query.useMutation) routes a GET operation to a useMutation hook as well. This is useful for GET endpoints that you want to trigger imperatively rather than on render.

Set to false to suppress Mutation hook generation; pair with useQuery: true if you want non-GET operations to be generated as Query hooks instead. When both useQuery and useMutation resolve to true for the same operation, the Mutation hook wins for GET and the Query hook wins for non-GET.

useInfinite

Type: Boolean

Default: unset — opt-in.

Generate useInfiniteQuery hooks. When set globally, this only applies to GET operations; per-operation overrides (override.operations.<id>.query.useInfinite) bypass that restriction for individual operations.

useSuspenseInfiniteQuery

Type: Boolean

Default: unset — opt-in.

Generate useSuspenseInfiniteQuery hooks. When set globally, this only applies to GET operations; per-operation overrides (override.operations.<id>.query.useSuspenseInfiniteQuery) bypass that restriction for individual operations.

useInfiniteQueryParam

Type: String | String[]

Query parameter name for infinite queries. An operation only gets an infinite hook when it declares the configured parameter.

Pass an array when a single spec paginates in more than one way. The names are candidates in priority order and are resolved per operation: the first one the operation actually declares becomes its page parameter, and an operation matching none of them gets no infinite hook.

An override.operations entry replaces the global value entirely, so a single operation can opt into a different candidate list.

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        query: {
          useInfinite: true,
          useInfiniteQueryParam: ['page', 'cursor'],
        },
        operations: { 
          listBets: {
            useInfinite: true,
            useInfiniteQueryParam: "cursor.marker",
          }
        }
      },
    },
  },
});

usePrefetch

Type: Boolean

Generate prefetch functions for SSR.

useInvalidate

Type: Boolean

Generate query invalidation helpers.

useSetQueryData

Type: Boolean

Generate type-safe helpers that update cached query data via setQueriesData.

Query keys are matched by prefix, so query params and body arguments are widened to accept undefined. Passing undefined updates every cached entry that shares the same path.

useGetQueryData

Type: Boolean

Generate type-safe getQueryData helpers.

mutationInvalidates

Type: Array

Automatically invalidate or reset queries on mutation success (Angular Query, React Query & Svelte Query):

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        query: {
          useInvalidate: true,
          mutationInvalidates: [
            {
              onMutations: ['createPets'],
              invalidates: ['listPets'],
            },
            {
              onMutations: ['deletePet', 'updatePet'],
              invalidates: [
                'listPets',
                { query: 'showPetById', params: ['petId'], invalidationMode: 'reset' },
                { query: 'adminPets', file: './admin' },
              ],
            },
          ],
        },
      },
    },
  },
});

Each entry in params is either a variable reference (string) or a literal value ({ literal: string }):

SyntaxGenerated code
params: ['petId']getShowPetByIdQueryKey(variables.petId)
params: [{ literal: '@me' }]getShowPetByIdQueryKey('@me')

Use { literal: "..." } for fixed values like "@me" that are not taken from mutation variables:

orval.config.ts
mutationInvalidates: [
  {
    onMutations: ['updateProfile'],
    invalidates: [
      { query: 'getProfile', params: [{ literal: '@me' }] },
    ],
  },
],

When a user provides their own onSuccess callback, both the auto-invalidation and the user callback run — the generated onSuccess composes them together. To opt out of auto-invalidation at runtime, pass skipInvalidation: true:

// Default: invalidation + user callback both run
const deletePet = injectDeletePet({
  mutation: { onSuccess: () => showToast('Deleted!') },
});

// Skip auto-invalidation and handle it manually
const deletePet = injectDeletePet({
  mutation: {
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: getListPetsQueryKey() });
    },
  },
  skipInvalidation: true,
});

signal

Type: Boolean

Include abort signal in queries.

queryKey / queryOptions / mutationOptions

Type: String | Object

Custom query/mutation key or options functions.

When a queryOptions or mutationOptions mutator declares a third parameter, orval passes operation identity so the mutator can branch on it (for example, to attach per-operation metadata or invalidate by operationId). The exact shape differs between the two:

  • queryOptions mutator — { url, operationId, operationName }
  • mutationOptions mutator — { operationId, operationName } (the url is supplied in the second parameter)

Each option takes a mutator, so point it at a file and an exported name:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      client: 'react-query',
      override: {
        query: {
          queryKey: {
            path: './src/mutators/custom-query-key.ts',
            name: 'customQueryKey',
          },
          queryOptions: {
            path: './src/mutators/custom-query-options.ts',
            name: 'customQueryOptions',
          },
          mutationOptions: {
            path: './src/mutators/custom-mutation.ts',
            name: 'useCustomMutation',
          },
        },
      },
    },
  },
});

queryKey replaces the generated key factory. It receives the operation's query properties and a context carrying the url:

src/mutators/custom-query-key.ts
export function customQueryKey(
  // The properties dictionary varies per operation (params, petId, ...), so
  // type it broadly if one mutator serves every endpoint.
  properties: Record<string, unknown>,
  context: { url: string },
) {
  return ['tenant-abc', context.url, properties] as const;
}

queryOptions wraps the options object passed to the generated hook. The third parameter is the operation identity described above:

src/mutators/custom-query-options.ts
import type { QueryKey } from '@tanstack/react-query';

export function customQueryOptions<T extends { queryKey: QueryKey }>(
  options: T,
  _queryProperties: Record<string, unknown>,
  operation: { url: string; operationId: string; operationName: string },
): T & { queryKey: QueryKey } {
  return {
    ...options,
    queryKey: ['operation', operation.operationId, ...options.queryKey],
  };
}

mutationOptions works the same way for mutations, which is where branching on operationId is most useful.

The mutator runs where the options object is built, inside the hook body, so side effects belong in a callback rather than in the mutator itself. Calling invalidateQueries() directly would fire on every render instead of after the mutation succeeds:

src/mutators/custom-mutation.ts
import { type UseMutationOptions, useQueryClient } from '@tanstack/react-query';

export const useCustomMutation = <TData, TError, TVariables, TContext>(
  options: UseMutationOptions<TData, TError, TVariables, TContext>,
  _: { url: string },
  operation: { operationId: string; operationName: string },
) => {
  const queryClient = useQueryClient();
  if (operation.operationId !== 'deletePetById') return options;

  return {
    ...options,
    onSuccess: (...args: Parameters<NonNullable<typeof options.onSuccess>>) => {
      queryClient.invalidateQueries({ queryKey: ['/pets'] });
      // Keep whatever the caller already passed.
      return options.onSuccess?.(...args);
    },
  };
};

Because the mutator receives operationId, one file can serve every operation and branch where the behaviour needs to differ, which is the usual alternative to a per-operation configuration option.

Controlling the use / get prefix on query options

By default orval names the exported query options factory use<Operation>QueryOptions when a queryOptions mutator (or a hook mutator) is configured, and get<Operation>QueryOptions otherwise — the use prefix is meant to signal a hook. Sometimes the factory is consumed outside a React component, for example in a router loader or in prefetchQuery / ensureQueryData, where a use* name is misleading and can trip rules-of-hooks lint rules. In that case, name the exported function yourself via the queryOptions mutator and the generated code will use your name verbatim:

src/mutators/custom-query-options.ts
export function customQueryOptions<T extends { queryKey: QueryKey }>(
  options: T,
): T {
  return options;
}
orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      client: 'react-query',
      override: {
        query: {
          queryOptions: {
            path: './src/mutators/custom-query-options.ts',
            name: 'customQueryOptions',
          },
        },
      },
    },
  },
});

The generated hook then calls getPetstoreQueryOptions(...) (the name of the mutator export) instead of an auto-derived usePetstoreQueryOptions, and it can be used safely with prefetchQuery / ensureQueryData:

export async function loader(queryClient: QueryClient) {
  return queryClient.ensureQueryData(getPetstoreQueryOptions());
}

The use/get prefix only affects the name of the exported options factory. The hook itself is still exported as use<Operation> and must be called from a React component.

shouldExportMutatorHooks

Type: Boolean Default: true

Export mutator hooks.

shouldExportQueryKey

Type: Boolean Default: true

Export query keys.

shouldFilterQueryKey

Type: Boolean Default: false

Add .filter(q => q !== undefined) to the query key. If false, as const is added instead. The filter can be adjusted with the queryKeyFilter option

When shouldFilterQueryKey is true:

endpoints.ts
export const getShowPetByIdQueryKey = (petId: string) => {
  return ['pets', petId].filter(q => q !== undefined);
};

When shouldFilterQueryKey is false:

endpoints.ts
export const getShowPetByIdQueryKey = (petId: string) => {
  return ['pets', petId] as const;
};

queryKeyFilter

Type: String Default: 'q => q !== undefined'

Adjusts how the queryKey is filtered, when shouldFilterQueryKey is true. Default is 'q => q !== undefined', which will result in it ending up beeing

endpoints.ts
.filter(q => q !== undefined)

One option could be to only make it filter out all falsy keys:

orval.config.ts
  shouldFilterQueryKey: true,
  queryKeyFilter: 'Boolean'

which would result in the generated code being

endpoints.ts
.filter(Boolean)

shouldSplitQueryKey

Type: Boolean Default: false

Generate query keys as arrays instead of strings.

useOperationIdAsQueryKey

Type: Boolean Default: false

Use operation ID instead of route path for query keys.

version

Type: Number Default: Detected from package.json

Force a specific version for generated hooks.

runtimeValidation

Type: Boolean Default: false

Enable Zod runtime validation for Angular query responses. Requires schemas: { type: 'zod' }. When enabled, responses are validated via Schema.parse() in the RxJS pipeline. Skipped for primitive types and custom mutators.


override.swr

SWR options:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        swr: {
          useInfinite: true,
          useSuspense: true,
          generateErrorTypes: false,
          swrOptions: {
            dedupingInterval: 10000,
          },
          swrMutationOptions: {
            revalidate: true,
          },
          swrInfiniteOptions: {
            initialSize: 10,
          },
        },
      },
    },
  },
});

useInfinite

Type: Boolean

Generate useSWRInfinite hooks.

useSWRMutationForGet

Type: Boolean

Generate useSWRMutation for GET requests.

useSuspense

Type: Boolean Default: false

Generate Suspense-compatible hooks.

generateErrorTypes

Type: Boolean Default: false

Generate custom error type aliases.

swrOptions / swrMutationOptions / swrInfiniteOptions

Type: Object

Override SWR hook options.


override.zod

Zod schema generation options:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        zod: {
          variant: 'mini',
          version: 4,
          strict: {
            response: true,
            query: true,
            param: true,
            header: true,
            body: true,
          },
          coerce: {
            query: ['string', 'number', 'boolean'],
          },
          generate: {
            param: true,
            body: true,
            response: true,
            query: true,
            header: true,
          },
          generateEachHttpStatus: true,
          useBrandedTypes: true,
          generateReusableSchemas: true,
          generateDiscriminatedUnion: true,
        },
      },
    },
  },
});

variant

Type: 'classic' | 'mini' — defaults to 'classic'

Select the generated Zod API style.

ValueOutput
'mini'Import from zod/mini and emit Zod Mini's functional/check-based API.
'classic'Import from zod and emit the regular chainable Zod API.

'classic' is the default to avoid changing existing projects. Prefer 'mini' when startup time, memory usage, or bundle size matter.

Zod Mini requires Zod 4 output. If variant: 'mini' is used with version: 3, or with version: 'auto' resolving to Zod 3, Orval throws instead of generating invalid output.

version

Type: 3 | 4 | 'auto' — defaults to 'auto'

Pin the Zod major version that generated output targets, so generation is deterministic instead of inferred from the installed zod package.

ValueOutput
4Always emit Zod 4 syntax (z.strictObject, z.iso.datetime(), .meta(), …).
3Always emit Zod 3-compatible syntax (.strict(), z.string().datetime(), …).
'auto'Infer from the resolved zod version; fall back to Zod 4 when none is detected.

Unlike most override.zod options, version is output-wide and cannot be set per operation or tag. See the Zod guide for details.

strict

Type: Object

Enable strict mode for schemas.

coerce

Type: Object

Enable coercion for specified types.

generate

Type: Object

Control which schemas are generated.

preprocess

Type: Object

Add preprocess functions to schemas.

params

Type: Mutator

Inject a Zod params argument (e.g. { error: ... }) into every generated validator. The referenced function is called once per validator at schema construction time and receives codegen-time context (operation, location, schema name, field path, validator name). Whatever it returns is passed as the trailing argument of the call.

Useful for i18n error keys, branded error messages, or any field-aware customisation that Zod's global error map cannot disambiguate on its own (because issue.path does not carry operation/schema identity).

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        zod: {
          params: { path: './zod-params.ts', name: 'zodParams' },
        },
      },
    },
  },
});
zod-params.ts
import type { ZodParamsContext } from 'orval';
import { i18n } from './i18n';

export const zodParams = (ctx: ZodParamsContext) => ({
  error: (issue: { input: unknown; path: PropertyKey[] }) =>
    i18n.t(
      `errors.${ctx.schemaName}.${ctx.fieldPath.join('.')}.${ctx.validator}`,
      { value: issue.input },
    ),
});

The 'schema' location is used for shared component schemas emitted under generateReusableSchemas. Component schemas have no single owning operation, so operationId is the empty string in that case — branch on ctx.location === 'schema' if your error keys need to fall back to a schema-only namespace.

Generated output (excerpt):

import { zodParams } from './zod-params';

export const CreateUserBody = zod.object({
  email: zod
    .string(zodParams({ operationId: 'createUser', location: 'body', schemaName: 'CreateUserBody', fieldPath: ['email'], validator: 'string' }))
    .email(zodParams({ operationId: 'createUser', location: 'body', schemaName: 'CreateUserBody', fieldPath: ['email'], validator: 'email' })),
});

Injection scope:

  • Applied to base types (string, number, boolean, bigint, date, integer), constraints (min, max, gt, lt, multipleOf, regex, length), formats (email, url, uuid, hostname, datetime, time), and literal, enum, instanceof, stringFormat.
  • Skipped on modifiers (optional, nullable, nullish, default, describe) and structural calls (object, array, tuple, union, rest, passthrough, strict).
  • fieldPath only includes object property names, mirroring Zod's own issue.path. Array indices and tuple positions are not appended — the inner element of { tags: array<string> } and a top-level tags: string both see fieldPath: ['tags']. Use the validator field to distinguish a container ('array', 'tuple') from its element ('string', 'number').

For static messages, return an object with a string error: return { error: 'My message' }. The function may return undefined to fall back to Zod defaults for a specific call.

The { error } shape is Zod v4-only — on v3 it is silently ignored and the default message is used. If your project supports both Zod v3 and v4, return { message: 'My message' } instead, which works on both.

dateTimeOptions / timeOptions

Type: Object

Default (dateTimeOptions): { offset: true }

Configure Zod datetime/time validation options. dateTimeOptions defaults to { offset: true } so generated schemas accept RFC3339 timestamps with timezone offsets (e.g. 2026-03-27T12:00:00+01:00) — matching the OpenAPI format: date-time contract. Pass an explicit object to override (e.g. { offset: false } or { offset: true, precision: 3 }).

useBrandedTypes

Type: boolean

Default: false

Append .brand() to generated Zod schemas using the schema name as the brand identifier. For array request/response bodies, only the top-level array wrapper schema is branded — the exported *Item helper schema is not branded.

generateReusableSchemas

Type: boolean

Default: false

Emit one reusable Zod schema per OpenAPI #/components/schemas/* $ref instead of inlining. The exported name is the last $ref segment with namingConvention applied. Other schemas and operation files reference the export by name (cycles are wrapped in zod.lazy(() => Name) only on the edges that close them).

Behavior:

  • When schemas: is configured (string or { type: 'zod' }) and client: 'zod' is set, the schemas directory holds reusable Zod schemas instead of TypeScript types. The schema files default to a .zod.ts extension; use schemaFileExtension to override it independently from the global fileExtension if you keep both TS types and reusable Zod schemas in the same directory.
  • Operation files import the named exports — pure-$ref body/response wrappers (e.g. PetCreateBody) are skipped so consumers import the component schema directly.
  • $ref siblings: nullable, default, description chain onto the named ref (e.g. Pet.nullable().describe(...)). properties, example, and other non-chainable siblings fall back to inlining at that one site.
  • The namingConvention must produce valid JavaScript identifiers (camelCase, PascalCase, or snake_case). kebab-case is rejected with a clear error because it would emit dashed exports.
  • Trade-off — readOnly on shared component schemas: without this flag, request bodies generated from $ref schemas strip readOnly: true properties so they don't appear in input validators. With this flag on, request and response endpoints share the same exported schema, so readOnly properties remain in body validators too. Either avoid readOnly on component schemas you share between requests and responses, or split into separate request/response schemas in the OpenAPI source.

generateMeta

Type: boolean

Default: false

Attach registry metadata to generated component schemas via .meta() (zod v4 only): id is the schema name, plus description and deprecated when the OpenAPI schema provides them.

// override: { zod: { generateMeta: true } }
export const Pet = zod
  .object({ name: zod.string() })
  .meta({ id: 'Pet', description: 'A pet in the store', deprecated: true });

Behavior:

  • Applies only to component schemas emitted as named exports (schemas: { type: 'zod' }, or client: 'zod' + generateReusableSchemas). Operation wrapper schemas are left untouched, so registry ids stay unique.
  • id is always emitted; description/deprecated only when present. Property-level descriptions still use .describe().
  • zod v3 has no .meta() — the option is a no-op there, and descriptions continue to emit via .describe().
  • The registry id makes z.toJSONSchema() reference the schema as #/$defs/<id>, round-tripping the component structure.

generateDiscriminatedUnion

Type: boolean

Default: false

Emit a oneOf/anyOf that carries an OpenAPI discriminator as zod.discriminatedUnion(key, [...]) instead of a plain zod.union([...]). A discriminated union picks the branch by its discriminator value first, so validation errors point at the offending field (type.name) instead of collapsing into a single "no union member matched" at the union root.

// override: { zod: { generateDiscriminatedUnion: true } }
export const Pet = zod.discriminatedUnion('petType', [
  zod.object({ petType: zod.literal('cat'), meows: zod.boolean() }),
  zod.object({ petType: zod.literal('dog'), barks: zod.boolean() }),
]);

Behavior:

  • Opt-in. Left false, unions are emitted exactly as before, so existing output is unchanged.
  • Safe fallback. A discriminated union is emitted only when every branch can be represented as an object carrying a literal (const/enum) discriminator. If any branch is a non-object, a nested union, or lacks a literal discriminator, generation falls back to a plain zod.union([...]) rather than emitting code that throws at construction.
  • Inheritance (allOf). Branches composed with allOf are flattened into a single object so they remain valid discriminated-union options — this is the case that previously forced the feature to be reverted (#2085). With generateReusableSchemas, a branch that references an allOf schema stays a plain union (the referenced schema can't be guaranteed to be an object from the reference alone).
  • Works with both Zod v3 (>= 3.20) and v4, and with the mini variant.

exactOptional

Type: boolean

Default: false

Emit optional object properties with .exactOptional() (classic) / zod.exactOptional() (mini) instead of .optional(), so consumers compiling with exactOptionalPropertyTypes infer { x?: T } rather than { x?: T | undefined }.

// override: { zod: { exactOptional: true } }
export const Pet = zod.object({ name: zod.string().exactOptional() });

Behavior:

  • Opt-in. Left false, optional properties emit .optional() as before, so existing output is unchanged.
  • zod v4 only. zod v3 has no .exactOptional(), so the option is a no-op there and .optional() is emitted.
  • Applies to optional properties in both the classic and mini variants.

override.effect

Effect schema generation options:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        effect: {
          strict: {
            response: true,
            query: true,
            param: true,
            header: true,
            body: true,
          },
          generate: {
            param: true,
            body: true,
            response: true,
            query: true,
            header: true,
          },
          generateEachHttpStatus: true,
          useBrandedTypes: true,
        },
      },
    },
  },
});

strict

Type: Object

Enable strict mode for schemas.

generate

Type: Object

Control which schemas are generated.

useBrandedTypes

Type: boolean

Default: false

Append S.brand() to generated Effect schemas using the schema name as the brand identifier. For array request/response bodies, only the top-level array wrapper schema is branded.

exactOptional

Type: boolean

Default: false

Emit optional Struct properties with S.optionalWith(schema, { exact: true }) instead of S.optional(schema), so consumers compiling with exactOptionalPropertyTypes infer { x?: T } rather than { x?: T | undefined }.


override.angular

Angular client options:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        angular: {
          provideIn: 'root', // 'root' | 'any' | '' | false
          retrievalClient: 'httpClient',
          runtimeValidation: true,
          httpResource: {
            debugName: 'getPetByIdResource',
          },
        },
      },
    },
  },
});

override.angular is reserved for Angular generator settings such as retrieval mode, DI scope, and runtime validation. Angular-only request pipeline overrides like override.paramsFilter still live on override so they can also be applied consistently via override.operations[...] and override.tags[...], alongside mutator and paramsSerializer.

provideIn

Type: 'root' | 'any' | boolean Default: 'root'

Controls the Angular @Injectable({ providedIn }) scope for generated service classes.

provideIn affects generated service classes only. httpResource functions are plain exports, not injectables.

retrievalClient

Type: 'httpClient' | 'httpResource' | 'both' Default: 'httpClient'

Controls how retrieval-style Angular operations are generated.

  • httpClient: keep retrievals as injectable services backed by Angular HttpClient
  • httpResource: generate signal-first retrieval functions using Angular httpResource
  • both: keep HttpClient service methods and emit retrieval resources in a sibling *.resource.ts file

Mutation-style operations still use generated HttpClient service methods by default unless a per-operation override changes the classification.

client

Type: 'httpClient' | 'httpResource' | 'both'

Backward-compatible alias for retrievalClient. Prefer override.angular.retrievalClient in new configs to make the retrieval-only scope clearer.

runtimeValidation

Type: Boolean Default: false

Enable Zod runtime validation for Angular output. Requires schemas: { type: 'zod' }. This option is opt-in for backward compatibility.

  • For generated HttpClient services, eligible JSON body responses are validated via Schema.parse() in the RxJS pipeline.
  • For generated httpResource functions, eligible JSON resources receive a parse: Schema.parse option.

Validation is skipped for primitive types, non-JSON responses, observe: 'events' | 'response', and custom mutator paths that bypass the generated validation flow.

queryObjectSerialization

Type: 'spec' | 'legacy' Default: 'spec'

Controls how query parameters whose declared schema is a plain object are serialized when no paramsSerializer/paramsFilter is configured for the operation. See the Object query parameters guide for the full explanation and a worked example.

  • spec (default): honor the OpenAPI parameter's style/explodeform + explode: true (the OpenAPI default) spreads the object's properties as top-level query params, form + explode: false joins them into a single comma-separated value, and deepObject emits bracketed name[prop] keys.
  • legacy: restore the pre-#3705 behavior of silently dropping object-typed query params from the generated request.
orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        angular: {
          queryObjectSerialization: 'legacy',
        },
      },
    },
  },
});

Like retrievalClient/runtimeValidation, this can be set globally, per-tag (override.tags[...].angular), or per-operation (override.operations[...].angular). It has no effect when a paramsSerializer or paramsFilter is configured — those remain in full control of the raw value. See issue #3705.

httpResource

Type: Object

Options forwarded into generated httpResource calls.

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        angular: {
          retrievalClient: 'httpResource',
          httpResource: {
            defaultValue: { id: 'fallback' },
            debugName: 'getPetByIdResource',
            injector: 'inject(Injector)',
            equal: '(a, b) => a?.id === b?.id',
          },
        },
      },
    },
  },
});

defaultValue

Type: unknown

Initial value exposed while the resource is idle/loading. When configured, generated overloads return HttpResourceRef<T> instead of HttpResourceRef<T | undefined>.

debugName

Type: String

Name shown in Angular DevTools.

injector

Type: String

Raw expression passed to HttpResourceOptions.injector.

equal

Type: String

Raw expression passed to HttpResourceOptions.equal.

baseUrl

Type: Object

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        angular: {
          baseUrl: {
            apiId: 'petstore',
          },
        },
      },
    },
  },
});

Opt-in: compose this output's runtime base URL through Angular dependency injection (an InjectionToken) instead of baking a static prefix into every generated route string. See the Angular guide for the full precedence chain, the generated artifacts, and a multi-API gateway-routing example.

angular-client only. Setting baseUrl on any other client logs a warning and has no effect.

apiId

Type: String (required)

Explicit, stable identifier for this API. Must match /^[A-Za-z][A-Za-z0-9_-]*$/; Orval throws a config-time error otherwise. apiId is never derived from the specification's info.title or the target file name — it drives every generated identifier, so it needs to stay stable across regenerations:

Generated identifierDerivation
<API_ID>_SERVER_URLEmbedded fallback URL constant
<API_ID>_BASE_URL_RESOLVERInjectionToken for the runtime resolver hook
<API_ID>_BASE_URLInjectionToken for the composed, normalized base URL
<Api>BaseUrlResolverContextResolver context type ({ apiId, serverUrl })
<Api>BaseUrlResolverResolver function type
provide<Api>BaseUrl(baseUrl)Directly provides the base URL, bypassing the resolver
provide<Api>BaseUrlResolver(resolver)Provides a custom resolver

<API_ID> is apiId upper-snake-cased (e.g. petstorePETSTORE); <Api> is apiId PascalCased (e.g. petstorePetstore).

index

Type: Number Default: 0

Which entry of the specification's servers array to embed as the default fallback URL, same semantics as baseUrl.index on the top-level baseUrl option.

variables

Type: Record<string, string>

Values for any {variable} placeholders in the selected server URL.

Error and warning behavior

  • Missing/invalid apiId — throws `override.angular.baseUrl.apiId` must be a non-empty string matching /^[A-Za-z][A-Za-z0-9_-]*$/ at config-normalization time.
  • Combined with output.baseUrl — throws: `override.angular.baseUrl` cannot be combined with the top-level `output.baseUrl`. Remove output.baseUrl from the output; the token's fallback already reads the specification's servers field, and a runtime override belongs in a provided resolver.
  • Set on a non-angular client — logs a warning and is otherwise ignored.
  • Set under override.operations[...].angular or override.tags[...].angular — logs a warning and is ignored. baseUrl is an output-level concern configured once via override.angular.baseUrl, not per operation or tag.

override.hono

Hono server options:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        hono: {
          handlers: 'src/handlers',
          handlerGenerationStrategy: 'smart',
          validatorOutputPath: 'src/validator.ts',
          compositeRoute: 'src/routes.ts',
        },
      },
    },
  },
});

handlers

Type: String

Changes output path for Hono handlers.

handlerGenerationStrategy

Type: 'smart' | 'skip' | 'full'

Default: 'smart'

Controls how an existing handler file is treated when you re-run orval. A file that does not exist yet is always generated fresh.

  • smart (default) — non-destructively reconcile only the parts orval owns: its own imports (names, module paths, casing) and the zValidator(...) arguments, and append handlers for new operations. Your custom imports, middleware, handler bodies, and top-level helpers are preserved. Requires the optional typescript peer dependency (see note below); if it is absent, smart falls back to skip with a warning.
  • skip — leave an existing handler file byte-for-byte unchanged. New operations still get fresh files (in split mode).
  • full — rebuild the file header, imports, and validator chain from the spec, splicing back only each handler body. Destructive: custom imports, middleware, and top-level helpers are dropped. Use only if you keep handlers minimal and want maximal sync with the spec.

smart and full use the TypeScript compiler API to parse existing handler files. typescript is an optional peer dependency — virtually every orval project already has it, so nothing extra is installed.

If output.clean is enabled and the handlers directory lives under the output target directory, handler files are deleted before generation runs, which defeats smart/skip preservation. Disable clean (or scope it) when relying on handler preservation.

validatorOutputPath

Type: String

Changes the validator output path.

compositeRoute

Type: String

Generate a combined routes file.


override.mcp

MCP server options:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        mcp: {
          server: {
            path: './custom-server.ts',
            name: 'customServer',
          },
        },
      },
    },
  },
});

server

Type: String | Object

Custom server function to use instead of the default StdioServerTransport. When set, the generated server.ts calls the function with createMcpServer.

Example implementation using @hono/mcp:

custom-server.ts
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StreamableHTTPTransport } from '@hono/mcp';
import { Hono } from 'hono';

export const customServer = (createMcpServer: () => McpServer) => {
  const app = new Hono();
  const server = createMcpServer();
  const transport = new StreamableHTTPTransport();

  app.all('/mcp', async (c) => {
    if (!server.isConnected()) {
      await server.connect(transport);
    }
    return transport.handleRequest(c);
  });

  Bun.serve({ fetch: app.fetch, port: Number(process.env.PORT ?? 3000) });
};

override.fetch

Fetch client options:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        fetch: {
          includeHttpResponseReturnType: false,
          forceSuccessResponse: true,
        },
      },
    },
  },
});

includeHttpResponseReturnType

Type: Boolean Default: true

Include HTTP status in return type. Set to false to return data directly.

forceSuccessResponse

Type: Boolean Default: false

Throw on error responses instead of returning them.

serializeResponseHeaders

Type: Boolean Default: false

Return response headers as a plain Record<string, string> instead of a Headers instance. Enable it when a response is cached across a serialization boundary — a Headers instance in dehydrate() state makes a Next.js Server Component fail with Only plain objects can be passed to Client Components from Server Components. Requires includeHttpResponseReturnType (the default).

Header names are lowercased and repeated headers are joined with , . set-cookie is dropped, because a dehydrated cache is embedded in the RSC payload and upstream session cookies must not travel with it. Only headers is converted: blob responses still return a Blob, and application/x-ndjson responses still return the raw Response under stream.

With a custom mutator the option changes the generated type but not the runtime value, because the mutator issues the request itself. The mutator must return headers that already match the declared shape — as it must for includeHttpResponseReturnType today.

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      client: 'react-query',
      httpClient: 'fetch',
      override: {
        fetch: {
          serializeResponseHeaders: true,
        },
      },
    },
  },
});

jsonReviver

Type: String | Object

Custom JSON reviver function (useful for date parsing).

runtimeValidation

Type: Boolean Default: false

Enable Zod runtime validation for fetch client responses. Requires schemas: { type: 'zod' }. When enabled, JSON responses are validated via Schema.parse() before being returned.

arrayFormat

Type: 'repeat' | 'brackets' | 'comma'

Controls how array query parameters are serialized when the OpenAPI spec does not explicitly set explode on a parameter. The spec's own explode property always takes precedence.

ValueOutput
repeat?tags=a&tags=b
brackets?tags[]=a&tags[]=b
comma?tags=a%2Cb
orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      client: 'fetch',
      override: {
        fetch: {
          arrayFormat: 'repeat',
        },
      },
    },
  },
});

For full control over serialization (including custom encoding, nested objects, etc.) use override.paramsSerializer instead.

useRuntimeFetcher

Type: Boolean Default: false

Allow injecting a custom fetch function at runtime. When enabled, generated request functions accept an optional fetchFn parameter and call (fetchFn ?? fetch)(...) instead of the global fetch(...). For query client hooks, a fetcher field is added to the options type. Has no effect on operations that use a custom mutator.

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      client: 'react-query', // also works with 'fetch', 'vue-query', 'svelte-query'
      httpClient: 'fetch',
      override: {
        fetch: {
          useRuntimeFetcher: true,
        },
      },
    },
  },
});

Generated output example

// Request function — gains a fetchFn parameter
export const listPets = async (
  params: ListPetsParams,
  options?: RequestInit,
  fetchFn?: typeof globalThis.fetch,
): Promise<listPetsResponse> => {
  const res = await (fetchFn ?? fetch)(getListPetsUrl(params), {
    ...options,
    method: 'GET',
  });
  // ...
};

// Query hook — gains a fetcher field in options
export const useListPets = (
  params: ListPetsParams,
  options?: {
    query?: UseQueryOptions<...>;
    fetch?: RequestInit;
    fetcher?: typeof globalThis.fetch;
  },
) => { ... };

Usage — SSR with request-scoped fetch

// SvelteKit — +page.ts
export const load = async ({ fetch }) => {
  const queryClient = new QueryClient();
  await prefetchListPetsQuery(queryClient, params, { fetcher: fetch });
  return { queryClient };
};

Runtime validation support matrix

runtimeValidation support differs by client family and mutator usage:

ClientConfig keyStatusNotes
angularoverride.angular.runtimeValidationValidates JSON body responses via Schema.parse(); skips primitive/void, observe: 'events'/'response', and custom mutator paths
angular-queryoverride.query.runtimeValidationValidates eligible responses via Schema.parse() in RxJS pipeline; skips primitive/void and custom mutator paths
fetchoverride.fetch.runtimeValidationValidates JSON responses via Schema.parse()
any client with custom mutatorvaries⚠️Runtime validation may be bypassed depending on mutator path and signature (see #2858)

override.mock

Mock generation overrides:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        mock: {
          properties: {
            '/tag|name/': 'jon',
            email: () => faker.internet.email(),
          },
          schemas: {
            Apple: {
              properties: {
                color: () => faker.helpers.arrayElement(['red', 'green']),
              },
            },
          },
          format: {
            email: () => faker.internet.email(),
            iban: () => faker.finance.iban(),
          },
          required: true,
          nonNullable: true,
          delay: 500,
          arrayMin: 1,
          arrayMax: 10,
          stringMin: 10,
          stringMax: 20,
          numberMin: 0,
          numberMax: 100,
        },
      },
    },
  },
});

properties

Override mock values per property path or regex. Applies to every schema that has a matching property.

schemas

Scope property overrides to a named schema, so the same property name can mock differently per schema (e.g. color on Apple vs. Car). Keyed by schema name; each entry holds a properties map using the same matching rules as properties (bare name, /regex/, exact #.path). Takes precedence over the global properties overrides. See the Faker guide for a worked example.

format

Provide custom generators for OpenAPI format values.

required

Type: Boolean

Make all properties required in mocks.

nonNullable

Type: Boolean Default: false

When true, nullable properties are generated without faker.helpers.arrayElement([value, null]). For OpenAPI 3.1 null-union array items (type: ['string', 'null']), this also skips null inside .map() callbacks. Optional properties may still be omitted via arrayElement([value, undefined]) unless required is also true. You can still pass null at runtime through the factory's overrideResponse argument.

delay

Type: Number | Function | false Default: false

Response delay in milliseconds. Set to false to remove delay.

fractionDigits

Type: Number Default: 2

Number of decimal places for floating-point numbers.

Array/String/Number Min/Max

Control generated data bounds (arrayMin, arrayMax, stringMin, stringMax, numberMin, numberMax).

useExamples

Type: Boolean Default: false

Use example property from OpenAPI specification for mock generation.

baseUrl

Type: String

Base URL for mock handlers.


override.operations

Override by operation ID:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        operations: {
          listPets: {
            mutator: 'src/response-type.js',
            query: {
              useQuery: true,
              useInfinite: false,
            },
            mock: {
              data: () => ({ id: 1, name: 'Buddy' }),
            },
          },
        },
      },
    },
  },
});

override.tags

Override by OpenAPI tag (same options as operations).


override.operationName

Type: Function

Custom function to override generated operation names.

Return string to control both the method name and the type-name base together:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        operationName: (operation, route, verb) => {
          return `custom_${operation.operationId}`;
        },
      },
    },
  },
});

Return [methodName, typeNameBase] to decouple method names from type-identifier names. This is useful for gateway-aggregated specs where multiple services share the same REST patterns (GET /products, GET /orders) — bare method names are safe per-tag (each service class scopes them), but type names (*Params, *Body, *Error, *Result) need to be globally unique to avoid barrel-level collisions with tags-split + splitByTags + indexFiles:

orval.config.ts
import { pascal } from '@orval/core';

export default defineConfig({
  api: {
    output: {
      mode: 'tags-split',
      schemas: { path: './model', splitByTags: true },
      override: {
        operationName: (_operation, route, verb) => {
          const segments = route.split('/').filter(Boolean);
          return [
            `${verb}${pascal(segments.slice(2).join('-'))}`,  // getProducts
            `${verb}${pascal(segments.slice(1).join('-'))}`,  // getCatalogProducts
          ];
        },
      },
    },
  },
});

Result:

// catalog/catalog.service.ts
class CatalogService {
  getProducts = (params: GetCatalogProductsParams) => ...;
}

// inventory/inventory.service.ts
class InventoryService {
  getProducts = (params: GetInventoryProductsParams) => ...;
}

The first element controls the function/hook name. The second controls the base for all operation-specific TypeScript type identifiers (*Params, *Body, *Error, *Result, *Accept, zod/hono/effect schema names).


override.components

Add suffixes to generated model names:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        components: {
          schemas: { suffix: 'DTO' },
          responses: { suffix: 'Response' },
          parameters: { suffix: 'Params' },
          requestBodies: { suffix: 'Bodies' },
        },
      },
    },
  },
});

Type Generation Options

useDates

Type: Boolean Default: false

Convert date/datetime to JavaScript Date objects.

useBigInt

Type: Boolean Default: false

Convert int64/uint64 to BigInt.

useTypeOverInterfaces

Type: Boolean Default: false

Use TypeScript type instead of interface.

useNamedParameters

Type: Boolean Default: false

Use named parameters object instead of positional arguments.

useDeprecatedOperations

Type: Boolean Default: true

Include deprecated operations.

enumGenerationType

Type: 'const' | 'enum' | 'union' Default: 'const'

How to generate enums:

// 'const' (default)
export const Example = { foo: 'foo', bar: 'bar' } as const;
export type Example = (typeof Example)[keyof typeof Example];

// 'enum'
export enum Example { foo = 'foo', bar = 'bar' }

// 'union'
export type Example = 'foo' | 'bar';

aliasCombinedTypes

Type: Boolean Default: false

Create intermediate type aliases for anyOf/oneOf/allOf.

suppressReadonlyModifier

Type: Boolean Default: false

Suppress readonly modifier on properties.

preserveReadonlyRequestBodies

Type: 'strip' | 'preserve' Default: 'strip'

Controls how Orval treats readonly properties when a schema is reused as a request body.

  • strip (recommended): removes readonly modifiers from generated request-body types via NonReadonly<T>. This is the safest default for most OpenAPI specifications because readOnly properties are response-oriented.
  • preserve: keeps readonly modifiers on generated request-body types. Use this only when your request DTOs are intentionally immutable and you want that immutability reflected in the generated TypeScript types.
orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        preserveReadonlyRequestBodies: 'strip',
      },
    },
  },
});

Tip

Prefer separate request and response schemas when your API semantics differ. This option is mainly useful when a single schema is reused for both request and response payloads.

This setting applies to request bodies regardless of the generated Angular style (HttpClient or httpResource). httpResource still sends request payloads, so the same request-body guidance applies.

useNullForOptional

Type: Boolean Default: false

Type optional properties as T | null instead of just T. Useful for JSON:API compatibility where null explicitly indicates "no value".

// Default (false)
export interface Pet {
  id: number;
  name?: string;
  tag?: string;
}

// With useNullForOptional: true
export interface Pet {
  id: number;
  name?: string | null;
  tag?: string | null;
}

factoryMethods

Type: Object Default: { generate: false }

Generate factory methods for DTOs (Data Transfer Objects) initialized with safe default values. Useful for testing and initializing empty state. Functionality handles OpenAPI readOnly and writeOnly flags to generate appropriate payload structures:

  • Required properties: Always included in the factory output, regardless of their visibility flags.
  • Optional readOnly properties: Always omitted from the factory output, as they would be dropped by the server.
  • Optional writeOnly properties: Always included in the factory output (even if includeOptionalProperty is set to false).
orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      factoryMethods: {
        functionNamePrefix: 'create',
        mode: 'split',
        includeOptionalProperty: true,
        outputDirectory: `#output.workspace.schemas`,
      },
    },
  },
});

functionNamePrefix

Type: String Default: 'create'

Prefix for the generated factory function names.

mode

Type: 'single' | 'split' | 'single-split' Default: 'split'

Where to generate the factory methods:

  • single: Appends the factory function to the schema file.
  • split: Creates a {schema}.factory.ts with factory method. By default it is placed next to schema file.
  • single-split: Aggregates all factory methods into a single factoryMethods.ts file.

includeOptionalProperty

Type: boolean Default: true

Determines whether optional schema properties are included in the default factory output.

outputDirectory

Type: String Default: #output.workspace.schemas

Defaults to the value configured in #output.workspace.schemas. Determines where factory methods will be generated (can be used to generated methods away from schema directory). Takes effect only when used mode is split or single-split.

Other Options

allParamsOptional

Type: Boolean Default: false

Make all parameters optional except path parameters.

urlEncodeParameters

Type: Boolean Default: false

Wrap each path parameter with encodeURIComponent(String(...)) in generated URL helpers. This option only affects path parameters; query parameters are typically encoded by the underlying client (URLSearchParams, axios, etc.).

Path parameters are stringified via String(value) before encoding, so array (style: simple|matrix|label) and object path parameters are not serialized according to their OpenAPI style — they fall back to the default String(value) representation.

optionsParamRequired

Type: Boolean Default: false

Make the options parameter required. Since the options parameter appears last in the parameter-set, any preceding parameters will also be required.

propertySortOrder

Type: 'Alphabetical' | 'Specification' Default: 'Specification'

How to sort properties in generated types.

$dynamicRef / $dynamicAnchor support

Orval automatically resolves JSON Schema 2020-12 $dynamicRef / $dynamicAnchor keywords in OpenAPI 3.1 specs. No configuration is needed.

Supported patterns

PatternDescription
Generic template emissionSchemas with $defs entries that have $dynamicAnchor but no $ref are emitted as TypeScript generic interfaces (e.g., interface PaginatedResponse<itemType>).
Type alias bindingSchemas that $ref a generic template and bind $defs entries with $dynamicAnchor + $ref are emitted as type aliases (e.g., type UserListResponse = PaginatedResponse<User>).
Self-referential $dynamicAnchorRecursive schemas where $dynamicRef resolves to the declaring schema itself (e.g., tree nodes).
allOf bound aliasesSchemas that combine a generic template reference with additional properties via allOf emit intersection types (e.g., type X = Template<Args> & { extra }).

Generic template example

Define a reusable generic schema with an unbound $dynamicAnchor in $defs:

OpenAPI 3.1 spec
components:
  schemas:
    PaginatedResponse:
      $defs:
        itemType:
          $dynamicAnchor: itemType
          not: {}
      type: object
      properties:
        items:
          type: array
          items:
            $dynamicRef: '#itemType'
        total:
          type: integer

Then bind it to concrete types:

    UserListResponse:
      $defs:
        itemType:
          $dynamicAnchor: itemType
          $ref: '#/components/schemas/User'
      $ref: '#/components/schemas/PaginatedResponse'

    OrderListResponse:
      $defs:
        itemType:
          $dynamicAnchor: itemType
          $ref: '#/components/schemas/Order'
      $ref: '#/components/schemas/PaginatedResponse'

Generated TypeScript:

export interface PaginatedResponse<itemType> {
  items: itemType[];
  total: number;
}

export type UserListResponse = PaginatedResponse<User>;
export type OrderListResponse = PaginatedResponse<Order>;

The generic parameter name (itemType) comes from the $dynamicAnchor value. The type alias name (UserListResponse) comes from the schema key in components.schemas. Endpoints that reference a bound alias use the alias name directly (e.g., Promise<AxiosResponse<UserListResponse>>).

Self-referential $dynamicAnchor example

When a schema declares $dynamicAnchor and uses $dynamicRef with the same anchor, the type resolves to itself:

OpenAPI 3.1 spec
components:
  schemas:
    BaseCategory:
      $dynamicAnchor: category
      type: object
      properties:
        id:
          type: string
        children:
          type: array
          items:
            $dynamicRef: '#category'

    LocalizedCategory:
      $dynamicAnchor: category
      allOf:
        - $ref: '#/components/schemas/BaseCategory'
        - type: object
          properties:
            displayName:
              type: string

Generated TypeScript:

export interface BaseCategory {
  id?: string;
  children?: BaseCategory[];
}

export interface LocalizedCategory {
  id?: string;
  children?: LocalizedCategory[];
  displayName?: string;
}

Each schema's $dynamicRef: '#category' resolves to its own type because it declares $dynamicAnchor: category.

Output layout with $dynamicRef generics

Generic templates, bound aliases, and their type arguments are all emitted as individual model files in the same shared location — they are never duplicated per tag. In tags and tags-split mode, every tag's service file imports from a single shared schema source.

With schemas configured and tags-split mode (indexFiles shown at its default, true):

models/
├── index.ts                      ← barrel re-exports everything (indexFiles: true)
├── apiEnvelopeTemplate.ts        ← generic template (ApiEnvelopeTemplate<T>)
├── paginatedTemplate.ts          ← generic template (PaginatedTemplate<T>)
├── pet.ts                        ← domain type
├── owner.ts                      ← domain type
├── paginatedPetItems.ts          ← bound alias (PaginatedTemplate<Pet>)
└── paginatedOwnerItems.ts        ← bound alias (PaginatedTemplate<Owner>)
pets/
└── pets.ts                       ← import { ... } from '../models'
owners/
└── owners.ts                     ← import { ... } from '../models'

With indexFiles: false, no barrel is generated and service files import each schema individually (e.g., import type { Pet } from '../models/pet').

Without a dedicated schemas directory, all schemas go into a single petstore.schemas.ts file at the output root.

When using input.filters.tags to filter endpoints, schemas referenced exclusively through $dynamicAnchor + $ref bindings in inline response $defs are automatically discovered and included in the output — no manual filters.schemas configuration is needed.

Limitations

  • Each schema in components.schemas is generated once with a single dynamic scope. If the same named component is referenced by multiple endpoints that each provide different $defs bindings, only one binding applies. The common pattern — putting $defs bindings on inline response schemas — works correctly.
  • $dynamicRef values targeting external documents (e.g., other.json#anchor) fall back to unknown.
  • Inline $defs entries without $ref that have $dynamicAnchor are treated as generic type parameters, not concrete bindings.

contentType

Filter content types:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        contentType: {
          include: ['application/json'],
          exclude: ['application/xml'],
        },
      },
    },
  },
});

splitByContentType

Type: Boolean Default: false

When an endpoint's requestBody supports multiple content types (e.g. application/json and multipart/form-data), generate a separate function for each content type instead of combining them into a single function with a union type parameter.

Each generated function is suffixed with the content type name (e.g. WithJson, WithFormData).

// Default (false) — single function with union body
updateProfile(body: FormDataType | JsonType) => { ... }

// With splitByContentType: true — separate function per content type
updateProfileWithFormData(body: FormDataType) => { ... }
updateProfileWithJson(body: JsonType) => { ... }
orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        splitByContentType: true,
      },
    },
  },
});

If the endpoint only has a single content type, no suffix is added and the behavior is the same as the default.

formData

Type: Boolean | String | Object

Customize form data generation. If an object is provided, specify path, name, and optionally default: true for default export.

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        formData: {
          path: './api/mutator/custom-form-data-fn.ts',
          name: 'customFormDataFn',
          // default: true
        },
      },
    },
  },
});
custom-form-data-fn.ts
export const customFormDataFn = <Body>(body: Body): FormData => {
  const formData = new FormData();
  // Custom implementation
  Object.entries(body as Record<string, any>).forEach(([key, value]) => {
    if (value !== undefined) {
      formData.append(key, value);
    }
  });
  return formData;
};

arrayHandling

Type: 'serialize' | 'serialize-with-brackets' | 'explode' Default: 'serialize'

Specifies how FormData handles arrays:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        formData: {
          arrayHandling: 'serialize-with-brackets',
        },
      },
    },
  },
});
  • serialize: formData.append('items', JSON.stringify(value))
  • serialize-with-brackets: formData.append('items[]', JSON.stringify(value))
  • explode: Expands nested objects with indexed keys

formUrlEncoded

Type: Boolean | String | Object

Customize form URL encoded data generation:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        formUrlEncoded: {
          path: './api/mutator/custom-form-url-encoded-fn.ts',
          name: 'customFormUrlEncodedFn',
        },
      },
    },
  },
});
custom-form-url-encoded-fn.ts
export const customFormUrlEncodedFn = <Body>(body: Body): URLSearchParams => {
  const params = new URLSearchParams();
  Object.entries(body as Record<string, any>).forEach(([key, value]) => {
    if (value !== undefined) {
      params.append(key, String(value));
    }
  });
  return params;
};

paramsSerializer

Type: String | Object

Note: Valid for Axios, Angular, and the fetch client.

Custom parameter serializer for query parameters. When set, the generated URL helper delegates query string building entirely to this function instead of using the built-in logic.

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        paramsSerializer: {
          path: './api/mutator/custom-params-serializer-fn.ts',
          name: 'customParamsSerializerFn',
        },
      },
    },
  },
});

For Axios and Angular the function receives the params object and can return any value Axios/Angular accepts. For the fetch client it must return a string (the raw query string without the leading ?):

custom-params-serializer-fn.ts
// Axios / Angular
export const customParamsSerializerFn = (
  params: Record<string, any>,
): string => {
  return Object.entries(params)
    .filter(([_, v]) => v !== undefined)
    .map(([k, v]) => `${k}=${encodeURIComponent(v)}`)
    .join('&');
};

// fetch client — must return a string
export const customParamsSerializer = (
  params: Record<string, unknown> | undefined,
): string =>
  new URLSearchParams(
    Object.entries(params ?? {})
      .filter(([_, v]) => v !== undefined)
      .flatMap(([k, v]) =>
        Array.isArray(v)
          ? v.map((item) => [k, String(item)])
          : [[k, String(v)]],
      ),
  ).toString();

paramsSerializerOptions

Type: Object

Note: Only valid when using Axios or Angular. Only used when paramsSerializer is not defined.

Use qs library for parameter serialization:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        paramsSerializerOptions: {
          qs: {
            arrayFormat: 'repeat',
          },
        },
      },
    },
  },
});

paramsFilter

Type: String | Object

Note: Only valid for the angular client, or angular-query when httpClient: 'angular'.

Replaces the built-in query-parameter filter that the Angular client applies before handing params to HttpParams. When set, orval does not strip null/undefined or non-primitive values for you — your function returns exactly the object HttpParams (or a configured paramsSerializer) receives.

This option intentionally lives at override.paramsFilter rather than override.angular.paramsFilter so the same request-shaping override can be used globally or narrowed per operation/tag, just like mutator and paramsSerializer.

When a paramsSerializer is configured, orval already preserves schema-declared object and array-of-object params so the serializer can handle them; without a serializer those params are dropped. See the Angular guide. Use paramsFilter when you need the raw object without a serializer, or for any control that the schema cannot express.

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        paramsFilter: {
          path: './api/mutator/custom-params-filter-fn.ts',
          name: 'customParamsFilterFn',
        },
      },
    },
  },
});
custom-params-filter-fn.ts
export const customParamsFilterFn = (
  params: Record<string, unknown>,
): Record<string, unknown> => {
  const result: Record<string, unknown> = {};
  for (const [key, value] of Object.entries(params)) {
    if (value !== undefined) {
      result[key] = value;
    }
  }
  return result;
};

useDates

Type: Boolean Default: false

Convert OpenAPI date or datetime to JavaScript Date objects instead of string.

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        useDates: true,
      },
    },
  },
});

Important: You must provide an Axios converter to convert serialized date strings to Date objects. This option only affects the TypeScript definition.

If you also want runtime conversion, prefer useDatesTransform below; the interceptor approach traverses every response and can convert date-looking strings that are not schema dates.

axios-instance.ts
import axios from 'axios';

const client = axios.create({ baseURL: '' });

client.interceptors.response.use((originalResponse) => {
  handleDates(originalResponse.data);
  return originalResponse;
});

export default client;

const isoDateFormat =
  /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d*)?(?:[-+]\d{2}:?\d{2}|Z)?$/;

function isIsoDateString(value: any): boolean {
  return value && typeof value === 'string' && isoDateFormat.test(value);
}

export function handleDates(body: any) {
  if (body === null || body === undefined || typeof body !== 'object')
    return body;

  for (const key of Object.keys(body)) {
    const value = body[key];
    if (isIsoDateString(value)) {
      body[key] = new Date(value); // default JS conversion
      // body[key] = parseISO(value); // date-fns conversion
      // body[key] = luxon.DateTime.fromISO(value); // Luxon conversion
    } else if (typeof value === 'object') {
      handleDates(value);
    }
  }
}

If using fetch client with useDates: true, query parameters of type Date are stringified using toISOString().

useDatesTransform

Type: Boolean Default: false

Use this property to also convert dates at runtime. While useDates only changes the generated TypeScript types, useDatesTransform additionally generates a small deserialize<OperationName>Response function for every operation whose response schema declares format: date or format: date-time fields, and chains it onto the generated call:

export const getOrderDetails = (orderId: string) => {
  return customInstance<OrderDetails>({ url: `/orders/${orderId}`, method: 'GET' }).then(
    deserializeGetOrderDetailsResponse,
  );
};

Only schema-declared date fields are converted — no response-wide traversal, no pattern matching on strings — and operations without date fields generate no extra code. Setting useDatesTransform: true implies useDates: true. This removes the need for the axios interceptor shown under useDates.

Current limitations: a oneOf/anyOf without an OpenAPI discriminator mapping is skipped, since there's no static way to tell which variant a given payload matched; a discriminated union with an explicit mapping is converted, emitting a switch on the discriminator property with one case per mapping key. additionalProperties maps are not converted, responses with multiple success shapes are skipped, and recursive schemas are left untouched entirely — converting only the levels above the cycle would leave deeper dates as strings while the generated types claim Date. Only the axios-based clients are wired (react-query, vue-query, svelte-query, and solid-query with httpClient: 'axios').

useBigInt

Type: Boolean Default: false

Convert OpenAPI int64 and uint64 format to JavaScript BigInt objects instead of number.

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        useBigInt: true,
      },
    },
  },
});

requestOptions

Type: Object | Boolean

Configure or remove request options. Set to false to remove entirely.

jsDoc.filter

Type: Function

Customize JSDoc generation by filtering and transforming schema entries:

orval.config.ts
export default defineConfig({
  petstore: {
    output: {
      override: {
        jsDoc: {
          filter: (schema) => {
            const allowlist = [
              'type', 'format', 'maxLength', 'minLength',
              'description', 'minimum', 'maximum', 'pattern',
              'nullable', 'enum',
            ];
            return Object.entries(schema || {})
              .filter(([key]) => allowlist.includes(key))
              .map(([key, value]) => ({ key, value }))
              .sort((a, b) => a.key.length - b.key.length);
          },
        },
      },
    },
  },
});

Result:

export interface Pet {
  /**
   * @type integer
   * @format int64
   */
  id: number;
  /**
   * @type string
   * @description Name of pet
   */
  name: string;
}

On this page

targetclientaxios clienthttpClientschemasString formObject formimportPathValidation of importPathsplitByTagsoperationSchemasfileExtensionschemaFileExtensionnamingConventionworkspacemodesinglesplittagstags-splitbaseUrlruntimeimportsgetBaseUrlFromSpecificationvariablesindexmockMocks OptionsMSW generator (type: 'msw')Faker generator (type: 'faker')schemasImportPathindexFilestagsSplitDeduplicationcommonTypesFileNamedocscleanformatterheaderstsconfigpackageJsonoverridetransformermutatortitlenamingConvention (property keys)headeroverride.queryuseQueryuseSuspenseQueryuseMutationuseInfiniteuseSuspenseInfiniteQueryuseInfiniteQueryParamusePrefetchuseInvalidateuseSetQueryDatauseGetQueryDatamutationInvalidatessignalqueryKey / queryOptions / mutationOptionsControlling the use / get prefix on query optionsshouldExportMutatorHooksshouldExportQueryKeyshouldFilterQueryKeyqueryKeyFiltershouldSplitQueryKeyuseOperationIdAsQueryKeyversionruntimeValidationoverride.swruseInfiniteuseSWRMutationForGetuseSuspensegenerateErrorTypesswrOptions / swrMutationOptions / swrInfiniteOptionsoverride.zodvariantversionstrictcoercegeneratepreprocessparamsdateTimeOptions / timeOptionsuseBrandedTypesgenerateReusableSchemasgenerateMetagenerateDiscriminatedUnionexactOptionaloverride.effectstrictgenerateuseBrandedTypesexactOptionaloverride.angularprovideInretrievalClientclientruntimeValidationqueryObjectSerializationhttpResourcedefaultValuedebugNameinjectorequalbaseUrlapiIdindexvariablesError and warning behavioroverride.honohandlershandlerGenerationStrategyvalidatorOutputPathcompositeRouteoverride.mcpserveroverride.fetchincludeHttpResponseReturnTypeforceSuccessResponseserializeResponseHeadersjsonReviverruntimeValidationarrayFormatuseRuntimeFetcherRuntime validation support matrixoverride.mockpropertiesschemasformatrequirednonNullabledelayfractionDigitsArray/String/Number Min/MaxuseExamplesbaseUrloverride.operationsoverride.tagsoverride.operationNameoverride.componentsType Generation OptionsuseDatesuseBigIntuseTypeOverInterfacesuseNamedParametersuseDeprecatedOperationsenumGenerationTypealiasCombinedTypessuppressReadonlyModifierpreserveReadonlyRequestBodiesuseNullForOptionalfactoryMethodsfunctionNamePrefixmodeincludeOptionalPropertyoutputDirectoryDefaults to the value configured in #output.workspace.schemas. Determines where factory methods will be generated (can be used to generated methods away from schema directory). Takes effect only when used mode is split or single-split.Other OptionsallParamsOptionalurlEncodeParametersoptionsParamRequiredpropertySortOrder$dynamicRef / $dynamicAnchor supportSupported patternsGeneric template exampleSelf-referential $dynamicAnchor exampleOutput layout with $dynamicRef genericsLimitationscontentTypesplitByContentTypeformDataarrayHandlingformUrlEncodedparamsSerializerparamsSerializerOptionsparamsFilteruseDatesuseDatesTransformuseBigIntrequestOptionsjsDoc.filter