Configuration files
How a generator finds its configuration file, what the file may contain, how defaults and specs combine, how several specifications are run, and what happens when something in the file is wrong. Read it before writing a configuration by hand; init (see getting started) writes a valid one for you, and this page explains every part of it.
The same rules apply to all four generators. Where this page says <generator>, substitute the command name: openapi-to-typescript, openapi-to-zod, openapi-to-zod-playwright or openapi-to-k6.
Where the generator looks
Run without --config, a generator looks in the current working directory for, in this order:
<generator>.config.ts<generator>.config.json- a
"<generator>"key inpackage.json
The first one found is used. Only the directory you run the command from is searched: a configuration in a parent directory is not picked up, so either run the generator from the directory that holds the file (an npm script does this for you) or point at it with -c. When nothing is found the run stops before generating anything:
No config file found. Searched for: openapi-to-zod.config.ts, openapi-to-zod.config.json, package.json (openapi-to-zod key)
Run 'openapi-to-zod init' to create a new config file.
The package.json form holds the same object a config file would, under the generator's name:
{
"name": "my-api-tests",
"openapi-to-zod": {
"specs": [{ "input": "specs/openapi.yaml", "outputTypes": "src/schemas.ts" }]
}
}
-c, --config <path> overrides discovery and accepts any file name:
npx openapi-to-zod --config config/zod.config.ts
A path that does not exist fails with Config file not found at: config/zod.config.ts. Whichever way the file was found, the paths inside it (input, outputTypes and the other output options) are resolved against the working directory, not against the configuration file.
There is no generate subcommand. Running npx <generator> with no subcommand generates; init is the only subcommand, and --help and --version are the only other flags common to all generators. reference/cli.md lists them, including the direct-mode flags that only openapi-to-k6 has.
TypeScript configuration files
A .ts configuration is transpiled with esbuild when it is loaded, so you need no ts-node or tsx of your own, and it may import modules. The imports resolve from the directory the configuration file is in, exactly as they would for any other file of your project. The one import every example on this site uses is defineConfig, which only returns its argument but gives your editor the full type of the configuration:
import { defineConfig } from "@spartaficial/openapi-to-zod";
export default defineConfig({
specs: [{ input: "specs/openapi.yaml", outputTypes: "src/schemas.ts" }],
});
The configuration must be the default export. Because imports are real imports, the package you import from has to be installed where the configuration lives. Running a generator in a directory without node_modules, or importing a package you have not added, stops with:
Cannot load C:\work\my-api-tests\openapi-to-zod.config.ts: the module '@spartaficial/openapi-to-zod' is not installed.
Install it where the config lives, or switch to a JSON config that needs no imports.
A TypeScript file is the better choice when you want autocompletion and type checking of option names, when a value is easier to express as code (a RegExp literal for customDateTimeFormatRegex, a path built from an environment variable), or when the file deserves comments.
JSON configuration files
A .json configuration holds the same object with no import and no comments. Everything that is a RegExp in TypeScript becomes a string, so backslashes are doubled:
{
"defaults": { "customDateTimeFormatRegex": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$" },
"specs": [{ "input": "specs/openapi.yaml", "outputTypes": "src/schemas.ts" }]
}
JSON is the right choice when the configuration is generated by another tool, or when the generator runs somewhere the project's dependencies are not installed.
The shape of a configuration
Every configuration, for every generator, is one object with up to three keys:
export default defineConfig({
defaults: {
// options shared by every entry in specs
},
specs: [
// one entry per specification, each with its input and output paths
],
executionMode: "parallel",
});
Nothing else is allowed at the top level. A flat object that puts input and outputTypes directly at the root is rejected as unrecognised keys, and specs must hold at least one entry:
- specs: Configuration must include at least one specification. Each specification should have 'input' and 'outputTypes' paths.
Each entry in specs names its specification and where to write the output. Which output fields are required depends on the generator:
| Generator | Required in every entry | Optional output fields |
|---|---|---|
openapi-to-typescript | input, outputTypes | |
openapi-to-zod | input, outputTypes | outputZodSchemas (then outputTypes holds types) |
openapi-to-zod-playwright | input, outputTypes, outputClient | outputService, outputZodSchemas |
openapi-to-k6 | input, outputClient, outputTypes | outputService |
An entry may also carry a name, a label of your own that the generators accept but do not currently print or use (see naming.md), and any option the generator supports. The generator pages describe what each option does; the reference pages list them all: options-typescript.md, options-zod.md, options-zod-playwright.md, options-k6.md.
How defaults and specs combine
Every key in defaults is copied into every entry of specs that does not set it itself. An entry's own value always wins, and a key an entry sets to undefined is treated as not set, so it does not erase the default. Most options are therefore written once:
export default defineConfig({
defaults: {
mode: "normal",
includeDescriptions: false,
stripSchemaPrefix: "Company.Models.",
},
specs: [
{ input: "specs/orders.yaml", outputTypes: "src/orders.ts" },
{ input: "specs/billing.yaml", outputTypes: "src/billing.ts", mode: "strict" },
],
});
Here orders.ts is generated in normal mode and billing.ts in strict mode; both drop descriptions and the schema prefix.
defaults accepts every option except the ones that identify a single specification: input, name and the output paths (outputTypes, outputZodSchemas, outputClient, outputService). Those are rejected rather than inherited, because inheriting an output path would make every entry write to the same file and the last one to finish would win. Putting one there is reported as an unknown key:
- defaults: Unrecognized key 'outputTypes'. Check for typos in field names.
Output paths and the output alias
outputTypes is the name of the main output in every generator. The zod and Playwright generators also accept output as a deprecated alias for it, so configurations written for earlier releases keep working. Using it prints one warning per run, however many entries use it:
[openapi-to-zod] Deprecation warning: 'output' is deprecated and will be removed in a future release. Use 'outputTypes' instead.
Setting both output and outputTypes to different values is an error. In the TypeScript and k6 generators output is not accepted at all; the error says what to use instead (Did you mean 'outputTypes'? The 'output' field was renamed to 'outputTypes'. and, for k6, Did you mean 'outputClient' or 'outputTypes'?).
In the zod and Playwright generators, outputZodSchemas splits the output into two files: the types go to outputTypes and the schemas to outputZodSchemas. Setting outputZodSchemas without outputTypes is rejected, because the schemas file imports its types from the other one. generators/zod.md explains when the split is worth it.
Running several specifications: executionMode
The entries in specs are run as a batch. executionMode chooses how:
| Value | Behaviour | Default for |
|---|---|---|
"parallel" | Entries are processed in batches of batchSize (default 10). The batchSize of the first entry applies to the whole run. | openapi-to-typescript, openapi-to-zod, openapi-to-k6 |
"sequential" | Entries are processed one after another. The Playwright generator writes several files per entry that depend on each other, so it takes this mode by default. | openapi-to-zod-playwright |
Either way the batch finishes every entry even when one fails. The failing entry is reported in place and again in the summary, and the command exits with status 1 when anything failed:
Executing 3 specification(s) in parallel (batch size: 10)...
Processing [1/3] specs/orders.yaml...
✓ Generated src/orders.ts
Processing [2/3] specs/empty.yaml...
✗ Failed to generate src/empty.ts: Empty or invalid OpenAPI specification in specs/empty.yaml
Processing [3/3] specs/billing.yaml...
✓ Generated src/billing.ts
==================================================
Batch Execution Summary
==================================================
Total specs: 3
Successful: 2
Failed: 1
Failed specs:
✗ specs/empty.yaml
Error: Empty or invalid OpenAPI specification in specs/empty.yaml
==================================================
Specifications in one batch must not write to the same file. Nothing checks this for you; with parallel execution the result would depend on timing.
Strict validation
The configuration is validated against the generator's schema before anything is generated, and the schema is strict: an unknown key anywhere, a value outside the allowed set, or a missing required field stops the run. The message names the path to each problem, so a typo is found in the file rather than in the generated output. This configuration misspells outputTypes and uses a mode that does not exist:
{
"defaults": { "mode": "lenient" },
"specs": [{ "input": "openapi.yaml", "outputType": "src/schemas.ts" }]
}
Invalid configuration file at: C:\work\my-api-tests\openapi-to-zod.config.json
Validation errors:
- defaults.mode: Invalid value '["strict","normal","loose"]'. Expected one of: strict, normal, loose
- specs.0: Unrecognized key 'outputType'. Check for typos in field names.
Please check your configuration file and ensure:
- All required fields are present (specs array with input/outputTypes)
- Field names are spelled correctly (no typos)
- Values match the expected types (e.g., mode: 'strict' | 'normal' | 'loose')
- No unknown/extra properties are included
specs.0 is the first entry of specs. All problems are reported together, so one run is enough to fix a file. Strictness also means an option that exists in one generator is an unknown key in another: schemaType is accepted by the TypeScript and zod generators and rejected by the Playwright and k6 generators, for the reasons given in request-and-response-shapes.md. reference/option-parity.md shows which generator accepts which option.
JSON or TypeScript?
| Need | Choose |
|---|---|
| Autocompletion and type errors in the editor | TypeScript |
RegExp literals, computed paths, environment variables | TypeScript |
| Comments next to options | TypeScript |
| No dependencies installed where the generator runs | JSON |
| The file is produced or edited by another tool | JSON |
Configuration kept in package.json | JSON |
Both produce identical output: the file format changes how the configuration is written, never what is generated.