Output
Output configuration options
target
Type: String
Output path for generated files.
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
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.
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
export default defineConfig({
petstore: {
output: {
schemas: './api/model',
},
},
});Object form
export default defineConfig({
petstore: {
output: {
schemas: {
path: './api/model',
type: 'typescript', // 'typescript' | 'zod'
},
},
},
});| Property | Type | Description |
|---|---|---|
path | string | Filesystem path for schema output |
type | string | 'typescript' (default) or 'zod' — optional |
importPath | string | Optional package import specifier (see below) |
splitByTags | boolean | Organize 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:
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 singleimportPath(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.importPathresolves to a single barrel file via tsconfig path mappings), setschemasImportPathon the faker generator to point faker factories at a separate import path. - If using factory methods (
factoryMethods), each schema is imported individually regardless ofindexFiles. - When
importPathis set, the relative-path computation infactoryMethods.outputDirectoryis bypassed: factories resolve imports against the package specifier rather than the on-disk factory output directory. - Config normalization rejects invalid
importPathvalues (empty, whitespace, relative, or absolute paths) before generation runs — see Validation ofimportPath.
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 likeC:\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).
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 dirsCross-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
PetimportsDogwhich importsDachshund, all three land in the same directory.
operationSchemas
Type: String
Separate path for operation-derived types (params, bodies, responses).
export default defineConfig({
petstore: {
output: {
schemas: './api/model',
operationSchemas: './api/model/params',
},
},
});fileExtension
Type: String
Default: .ts
Customize file extension for generated files:
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.tsschemaFileExtension
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:
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:
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:
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.tstags
One file per OpenAPI tag:
my-app/src/
├── pets.ts
└── petstore.schemas.tstags-split
Folder per tag with split files:
my-app/src/
├── petstore.schemas.ts
└── pets/
├── petstore.msw.ts
└── petstore.tsWhen 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 ../modelsTo 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.tsbaseUrl
Type: String | Object
Default: ''
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.
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:
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:
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).
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:
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:
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
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,
},
],
},
},
},
});| Option | Type | Default | Description |
|---|---|---|---|
indexMockFiles | Boolean | false | In 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. |
path | String | undefined | Shared 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. |
generators | Array<MockOptions | Function> | [] | One entry per output mock file. Each entry can be an object (MockOptions) or a custom ClientMockBuilder function. |
export default defineConfig({
petstore: {
output: {
mock: {
path: './src/api/mocks',
generators: [
{ type: 'msw', path: './src/api/mocks/msw' },
{ type: 'faker' },
],
},
},
},
});MSW generator (type: 'msw')
| Option | Type | Default | Description |
|---|---|---|---|
type | 'msw' | required | Discriminator for MSW handler generation. |
path | String | undefined | Output 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. |
operationResponses | Boolean | true | Emit 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. |
delay | Number | Function | false | false | Response delay in ms. |
delayFunctionLazyExecute | Boolean | false | Execute delay function at runtime instead of at build time. |
baseUrl | String | '' | Base URL for the generated MSW handlers. |
useExamples | Boolean | false | Use OpenAPI examples to seed response values. |
generateEachHttpStatus | Boolean | false | Generate response factories for every documented status code. |
locale | String | 'en' | Faker.js locale. |
preferredContentType | String | undefined | Preferred 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.
| Option | Type | Default | Description |
|---|---|---|---|
type | 'faker' | required | Discriminator for Faker-only output. |
path | String | undefined | Output 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. |
schemas | Boolean | false | Emit a consolidated mock factory file (get<SchemaName>Mock) for every entry under components/schemas. |
schemasImportPath | String | undefined | Package 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. |
operationResponses | Boolean | true | Emit per-operation response mock factories (the historical behavior). Set to false together with schemas: true to get only the consolidated schema factories. |
useExamples | Boolean | false | Use OpenAPI examples to seed response values. |
generateEachHttpStatus | Boolean | false | Generate response factories for every documented status code. |
locale | String | 'en' | Faker.js locale. |
preferredContentType | String | undefined | Preferred content type when an operation lists more than one. |
arrayItems | Boolean | false | Emit 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:
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.tsis generated with named re-exports for public shared types plusexport *re-exports for each per-tag implementation file
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.
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:
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.
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:
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.
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.
export default defineConfig({
petstore: {
output: {
headers: true,
},
},
});tsconfig
Type: String | Object
Custom TypeScript configuration path or inline config:
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:
export default defineConfig({
petstore: {
output: {
override: {
transformer: 'src/yourfunction.js',
},
},
},
});mutator
Type: String | Object
Custom HTTP client implementation:
export default defineConfig({
petstore: {
output: {
override: {
mutator: {
path: './api/mutator/custom-instance.ts',
name: 'customInstance',
},
},
},
},
});Example implementation:
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):
export default defineConfig({
petstore: {
output: {
override: {
title: (title) => `${title}Api`,
},
},
},
});namingConvention (property keys)
Type: Object
Change naming convention for property keys (not files):
export default defineConfig({
petstore: {
output: {
override: {
namingConvention: {
enum: 'PascalCase', // camelCase, PascalCase, snake_case, kebab-case
},
},
},
},
});header
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.
export default defineConfig({
petstore: {
output: {
override: {
header: (info) => [
`Generated by Orval`,
`Do not edit manually.`,
...(info.title ? [info.title] : []),
],
},
},
},
});override.query
TanStack Query options:
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.
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):
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 }):
| Syntax | Generated 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:
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:
queryOptionsmutator —{ url, operationId, operationName }mutationOptionsmutator —{ operationId, operationName }(theurlis supplied in the second parameter)
Each option takes a mutator, so point it at a file and an exported name:
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:
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:
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:
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:
export function customQueryOptions<T extends { queryKey: QueryKey }>(
options: T,
): T {
return options;
}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:
export const getShowPetByIdQueryKey = (petId: string) => {
return ['pets', petId].filter(q => q !== undefined);
};When shouldFilterQueryKey is false:
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
.filter(q => q !== undefined)One option could be to only make it filter out all falsy keys:
shouldFilterQueryKey: true,
queryKeyFilter: 'Boolean'which would result in the generated code being
.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:
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:
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.
| Value | Output |
|---|---|
'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.
| Value | Output |
|---|---|
4 | Always emit Zod 4 syntax (z.strictObject, z.iso.datetime(), .meta(), …). |
3 | Always 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).
export default defineConfig({
petstore: {
output: {
override: {
zod: {
params: { path: './zod-params.ts', name: 'zodParams' },
},
},
},
},
});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), andliteral,enum,instanceof,stringFormat. - Skipped on modifiers (
optional,nullable,nullish,default,describe) and structural calls (object,array,tuple,union,rest,passthrough,strict). fieldPathonly includes object property names, mirroring Zod's ownissue.path. Array indices and tuple positions are not appended — the inner element of{ tags: array<string> }and a top-leveltags: stringboth seefieldPath: ['tags']. Use thevalidatorfield 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' }) andclient: 'zod'is set, the schemas directory holds reusable Zod schemas instead of TypeScript types. The schema files default to a.zod.tsextension; useschemaFileExtensionto override it independently from the globalfileExtensionif you keep both TS types and reusable Zod schemas in the same directory. - Operation files import the named exports — pure-
$refbody/response wrappers (e.g.PetCreateBody) are skipped so consumers import the component schema directly. $refsiblings:nullable,default,descriptionchain 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
namingConventionmust produce valid JavaScript identifiers (camelCase,PascalCase, orsnake_case).kebab-caseis rejected with a clear error because it would emit dashed exports. - Trade-off —
readOnlyon shared component schemas: without this flag, request bodies generated from$refschemas stripreadOnly: trueproperties so they don't appear in input validators. With this flag on, request and response endpoints share the same exported schema, soreadOnlyproperties remain in body validators too. Either avoidreadOnlyon 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' }, orclient: 'zod'+generateReusableSchemas). Operation wrapper schemas are left untouched, so registryids stay unique. idis always emitted;description/deprecatedonly 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
idmakesz.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 plainzod.union([...])rather than emitting code that throws at construction. - Inheritance (
allOf). Branches composed withallOfare 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). WithgenerateReusableSchemas, a branch that references anallOfschema 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
minivariant.
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
minivariants.
override.effect
Effect schema generation options:
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:
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 AngularHttpClienthttpResource: generate signal-first retrieval functions using AngularhttpResourceboth: keepHttpClientservice methods and emit retrieval resources in a sibling*.resource.tsfile
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
HttpClientservices, eligible JSON body responses are validated viaSchema.parse()in the RxJS pipeline. - For generated
httpResourcefunctions, eligible JSON resources receive aparse: Schema.parseoption.
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'sstyle/explode—form+explode: true(the OpenAPI default) spreads the object's properties as top-level query params,form+explode: falsejoins them into a single comma-separated value, anddeepObjectemits bracketedname[prop]keys.legacy: restore the pre-#3705 behavior of silently dropping object-typed query params from the generated request.
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.
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
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 identifier | Derivation |
|---|---|
<API_ID>_SERVER_URL | Embedded fallback URL constant |
<API_ID>_BASE_URL_RESOLVER | InjectionToken for the runtime resolver hook |
<API_ID>_BASE_URL | InjectionToken for the composed, normalized base URL |
<Api>BaseUrlResolverContext | Resolver context type ({ apiId, serverUrl }) |
<Api>BaseUrlResolver | Resolver 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. petstore → PETSTORE);
<Api> is apiId PascalCased (e.g. petstore → Petstore).
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`. Removeoutput.baseUrlfrom the output; the token's fallback already reads the specification'sserversfield, and a runtime override belongs in a provided resolver. - Set on a non-
angularclient — logs a warning and is otherwise ignored. - Set under
override.operations[...].angularoroverride.tags[...].angular— logs a warning and is ignored.baseUrlis an output-level concern configured once viaoverride.angular.baseUrl, not per operation or tag.
override.hono
Hono server options:
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 thezValidator(...)arguments, and append handlers for new operations. Your custom imports, middleware, handler bodies, and top-level helpers are preserved. Requires the optionaltypescriptpeer dependency (see note below); if it is absent, smart falls back toskipwith a warning.skip— leave an existing handler file byte-for-byte unchanged. New operations still get fresh files (insplitmode).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:
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:
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:
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.
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.
| Value | Output |
|---|---|
repeat | ?tags=a&tags=b |
brackets | ?tags[]=a&tags[]=b |
comma | ?tags=a%2Cb |
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.
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:
| Client | Config key | Status | Notes |
|---|---|---|---|
angular | override.angular.runtimeValidation | ✅ | Validates JSON body responses via Schema.parse(); skips primitive/void, observe: 'events'/'response', and custom mutator paths |
angular-query | override.query.runtimeValidation | ✅ | Validates eligible responses via Schema.parse() in RxJS pipeline; skips primitive/void and custom mutator paths |
fetch | override.fetch.runtimeValidation | ✅ | Validates JSON responses via Schema.parse() |
| any client with custom mutator | varies | ⚠️ | Runtime validation may be bypassed depending on mutator path and signature (see #2858) |
override.mock
Mock generation overrides:
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:
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:
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:
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:
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 viaNonReadonly<T>. This is the safest default for most OpenAPI specifications becausereadOnlyproperties 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.
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
readOnlyproperties: Always omitted from the factory output, as they would be dropped by the server. - Optional
writeOnlyproperties: Always included in the factory output (even ifincludeOptionalPropertyis set tofalse).
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.tswith factory method. By default it is placed next to schema file.single-split: Aggregates all factory methods into a singlefactoryMethods.tsfile.
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
| Pattern | Description |
|---|---|
| Generic template emission | Schemas with $defs entries that have $dynamicAnchor but no $ref are emitted as TypeScript generic interfaces (e.g., interface PaginatedResponse<itemType>). |
| Type alias binding | Schemas 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 $dynamicAnchor | Recursive schemas where $dynamicRef resolves to the declaring schema itself (e.g., tree nodes). |
allOf bound aliases | Schemas 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:
components:
schemas:
PaginatedResponse:
$defs:
itemType:
$dynamicAnchor: itemType
not: {}
type: object
properties:
items:
type: array
items:
$dynamicRef: '#itemType'
total:
type: integerThen 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:
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: stringGenerated 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.schemasis generated once with a single dynamic scope. If the same named component is referenced by multiple endpoints that each provide different$defsbindings, only one binding applies. The common pattern — putting$defsbindings on inline response schemas — works correctly. $dynamicRefvalues targeting external documents (e.g.,other.json#anchor) fall back tounknown.- Inline
$defsentries without$refthat have$dynamicAnchorare treated as generic type parameters, not concrete bindings.
contentType
Filter content types:
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) => { ... }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.
export default defineConfig({
petstore: {
output: {
override: {
formData: {
path: './api/mutator/custom-form-data-fn.ts',
name: 'customFormDataFn',
// default: true
},
},
},
},
});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:
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:
export default defineConfig({
petstore: {
output: {
override: {
formUrlEncoded: {
path: './api/mutator/custom-form-url-encoded-fn.ts',
name: 'customFormUrlEncodedFn',
},
},
},
},
});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.
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 ?):
// 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
paramsSerializeris not defined.
Use qs library for parameter serialization:
export default defineConfig({
petstore: {
output: {
override: {
paramsSerializerOptions: {
qs: {
arrayFormat: 'repeat',
},
},
},
},
},
});paramsFilter
Type: String | Object
Note: Only valid for the
angularclient, orangular-querywhenhttpClient: '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.
export default defineConfig({
petstore: {
output: {
override: {
paramsFilter: {
path: './api/mutator/custom-params-filter-fn.ts',
name: 'customParamsFilterFn',
},
},
},
},
});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.
export default defineConfig({
petstore: {
output: {
override: {
useDates: true,
},
},
},
});Important: You must provide an Axios converter to convert serialized date strings to
Dateobjects. 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.
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
fetchclient withuseDates: true, query parameters of type Date are stringified usingtoISOString().
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.
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:
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;
}