Skip to content

Commit f0afbe0

Browse files
committed
feat: add option to inject params in generated zod schemas
1 parent 519da81 commit f0afbe0

5 files changed

Lines changed: 515 additions & 16 deletions

File tree

docs/content/docs/reference/configuration/output.mdx

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,6 +1080,67 @@ Control which schemas are generated.
10801080

10811081
Add preprocess functions to schemas.
10821082

1083+
### params
1084+
1085+
**Type:** [`Mutator`](#mutator)
1086+
1087+
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.
1088+
1089+
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).
1090+
1091+
```ts title="orval.config.ts"
1092+
export default defineConfig({
1093+
petstore: {
1094+
output: {
1095+
override: {
1096+
zod: {
1097+
params: { path: './zod-params.ts', name: 'zodParams' },
1098+
},
1099+
},
1100+
},
1101+
},
1102+
});
1103+
```
1104+
1105+
```ts title="zod-params.ts"
1106+
import { i18n } from './i18n';
1107+
1108+
type ZodParamsContext = {
1109+
operationId: string;
1110+
location: 'param' | 'query' | 'header' | 'body' | 'response';
1111+
schemaName: string;
1112+
fieldPath: string[];
1113+
validator: string;
1114+
};
1115+
1116+
export const zodParams = (ctx: ZodParamsContext) => ({
1117+
error: (issue: { input: unknown; path: PropertyKey[] }) =>
1118+
i18n.t(
1119+
`errors.${ctx.schemaName}.${ctx.fieldPath.join('.')}.${ctx.validator}`,
1120+
{ value: issue.input },
1121+
),
1122+
});
1123+
```
1124+
1125+
Generated output (excerpt):
1126+
1127+
```ts
1128+
import { zodParams } from './zod-params';
1129+
1130+
export const CreateUserBody = zod.object({
1131+
email: zod
1132+
.string(zodParams({ operationId: 'createUser', location: 'body', schemaName: 'CreateUserBody', fieldPath: ['email'], validator: 'string' }))
1133+
.email(zodParams({ operationId: 'createUser', location: 'body', schemaName: 'CreateUserBody', fieldPath: ['email'], validator: 'email' })),
1134+
});
1135+
```
1136+
1137+
Injection scope:
1138+
1139+
- Applied to base types (`string`, `number`, `boolean`, `bigint`, `date`, `integer`), constraints (`min`, `max`, `gt`, `lt`, `multipleOf`, `regex`, `length`), formats (`email`, `url`, `uuid`, `hostname`, `datetime`, `time`), and `literal`, `enum`, `instanceof`, `stringFormat`.
1140+
- Skipped on modifiers (`optional`, `nullable`, `nullish`, `default`, `describe`) and structural calls (`object`, `array`, `tuple`, `union`, `rest`, `passthrough`, `strict`).
1141+
1142+
Static messages are supported by returning a plain string: `return { error: 'My message' }`. The function may return `undefined` to fall back to Zod defaults for a specific call.
1143+
10831144
### dateTimeOptions / timeOptions
10841145

10851146
**Type:** `Object`

packages/core/src/types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,13 @@ export interface ZodOptions {
743743
body?: Mutator;
744744
response?: Mutator;
745745
};
746+
/**
747+
* Mutator referencing a function called once per emitted validator at schema
748+
* construction time. It receives codegen-time context (operation, location,
749+
* schema name, field path, validator name) and returns a Zod `params` object
750+
* (e.g. `{ error: ... }`) that is appended as the trailing argument.
751+
*/
752+
params?: Mutator;
746753
dateTimeOptions?: ZodDateTimeOptions;
747754
timeOptions?: ZodTimeOptions;
748755
generateEachHttpStatus?: boolean;
@@ -793,6 +800,7 @@ export interface NormalizedZodOptions {
793800
body?: NormalizedMutator;
794801
response?: NormalizedMutator;
795802
};
803+
params?: NormalizedMutator;
796804
generateEachHttpStatus: boolean;
797805
useBrandedTypes: boolean;
798806
generateReusableSchemas: boolean;

packages/orval/src/utils/options.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,14 @@ export async function normalizeOptions(
504504
}
505505
: {}),
506506
},
507+
...(outputOptions.override?.zod?.params
508+
? {
509+
params: normalizeMutator(
510+
workspace,
511+
outputOptions.override.zod.params,
512+
),
513+
}
514+
: {}),
507515
generateEachHttpStatus:
508516
outputOptions.override?.zod?.generateEachHttpStatus ?? false,
509517
useBrandedTypes:
@@ -890,6 +898,11 @@ function normalizeOperationsAndTags(
890898
}
891899
: {}),
892900
},
901+
...(zod.params
902+
? {
903+
params: normalizeMutator(workspace, zod.params),
904+
}
905+
: {}),
893906
generateEachHttpStatus: zod.generateEachHttpStatus ?? false,
894907
useBrandedTypes: zod.useBrandedTypes ?? false,
895908
generateReusableSchemas:

0 commit comments

Comments
 (0)