Skip to content

feat: add option to inject params in generated zod schemas - #3475

Merged
melloware merged 1 commit into
orval-labs:masterfrom
titivermeesch:feat/zod-param-injection
May 29, 2026
Merged

feat: add option to inject params in generated zod schemas#3475
melloware merged 1 commit into
orval-labs:masterfrom
titivermeesch:feat/zod-param-injection

Conversation

@titivermeesch

@titivermeesch titivermeesch commented May 28, 2026

Copy link
Copy Markdown
Contributor

Closes #3425

Adds override.zod.params to inject a Zod params argument (e.g. { error: ... }) into each generated validator. The option takes a Mutator (same shape as the existing preprocess) pointing to a function. Orval calls that function once per validator at schema construction time with codegen-time context (operation, location, schema name, field path, validator name), and emits the return value as the trailing argument of the call.

Summary by CodeRabbit

  • New Features

    • Added a Zod params option to inject build-time parameters into generated validators (global, per-operation/tag, and component schemas); re-exported the Zod params context type.
  • Documentation

    • Expanded Zod output docs with examples, where injection applies or is skipped, fieldPath semantics, and supported return shapes.
  • Tests

    • Added coverage for injection placement, field-path propagation, special-merge rules, reusable-schema behavior, and no-injection regression.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a new override.zod.params mutator that is normalized and threaded into Zod schema generation so a user-provided function can produce per-validator params arguments using build-time context (operation, location, schema name, fieldPath, validator).

Changes

Zod params injection feature

Layer / File(s) Summary
Configuration types and option normalization
packages/core/src/types.ts, packages/orval/src/utils/options.ts, packages/orval/src/index.ts, packages/orval/src/write-specs.ts
ZodOptions and NormalizedZodOptions gain an optional params mutator; global and per-operation zod.params are normalized via normalizeMutator. Public API re-exports ZodParamsContext and specs writer resolves/generates mutator for schemas.
Reusable schema wiring and imports
packages/orval/src/reusable-schemas.ts, packages/orval/src/write-zod-specs.ts, packages/orval/src/write-specs.ts
Adds paramsMutator option to reusable-schema generation, forwards mutator through inline/reusable generation paths, builds mutator import statements, and conditionally emits mutator import only when referenced by generated zod expressions.
Injection interface and field path threading infrastructure
packages/zod/src/index.ts
Adds ZodParamsInjection/ZodParamsContext types and buildParamsArg, and extends parse entrypoints to accept and propagate fieldPath.
Field path propagation through schema structures
packages/zod/src/index.ts
Propagates fieldPath through allOf merges/fallbacks, unions, additionalProperties, object properties, arrays, tuples, and .rest(...) so leaf validators receive full path context.
Validator call emission with params injection
packages/zod/src/index.ts
Refactors validator emission to compute formattedArgs, optionally build an injected params arg, merge it into existing options for select datetime/time/iso.* validators, or append it as an extra argument for other validators.
Route schema generation and mutator wiring
packages/zod/src/index.ts
Generates a paramsMutator from override.zod.params, implements makeParamsInjection(...), threads injections into param/query/header/body/response parsing, and includes paramsMutator in returned mutators.
Comprehensive test suite
packages/zod/src/zod.test.ts, packages/orval/src/reusable-schemas.test.ts
Adds unit and end-to-end tests verifying zodParams(...) injection at leaf validators with correct fieldPath/context, merging rules for certain validators, skipping wrappers, and absence behavior when mutator omitted.
User-facing documentation
docs/content/docs/reference/configuration/output.mdx
Documents preprocess.params config (invocation timing, context fields, injection/skip scope, return shapes), plus examples showing how params affects generated validators and error messages.

Sequence Diagram

sequenceDiagram
  participant User as User Config
  participant Orval as Orval Generator
  participant Parser as parseZodValidationSchemaDefinition
  participant Mutator as paramsMutator
  participant Output as Generated Code

  User->>Orval: Provide override.zod.params function
  Orval->>Orval: normalizeMutator(workspace, zod.params)
  Orval->>Parser: parseZodValidationSchemaDefinition(schema, paramsInjection)
  Parser->>Parser: Thread fieldPath through property parsing
  Parser->>Mutator: buildParamsArg({operationId, location, schemaName, fieldPath, validator})
  Mutator->>Mutator: Invoke user params function with context
  Mutator-->>Parser: Return params object or undefined
  Parser->>Output: Emit validator call with merged or appended params
  Output-->>User: Generated zod.* validator calls with params
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • orval-labs/orval#3465: Touches Zod reusable-schema generation and inline emission; related to the reusable-schema import/emit changes.
  • orval-labs/orval#3206: Previously extended generateZodSchemasInline flow; directly connected to threading optional paramsMutator through that path.

Suggested reviewers

  • melloware

🐰 I hop through schemas, thread by thread,
Injecting params where validators tread.
Build-time context in a tiny call,
Errors now tell the story for all.
🥕 Hooray — messages spring to life!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding an option to inject params in generated Zod schemas, which is the core feature implemented across the entire changeset.
Linked Issues check ✅ Passed The PR fully implements all coding requirements from issue #3425: adds params option to ZodOptions [#3425], threads mutator through parseZodValidationSchemaDefinition [#3425], supports per-operation/tag params via normalizeOptions [#3425], and includes comprehensive test coverage validating injection behavior [#3425].
Out of Scope Changes check ✅ Passed All changes directly support the params injection feature: type definitions, mutator normalization, injection logic in validator emission, test coverage, documentation, and re-exports are all within scope of issue #3425.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/content/docs/reference/configuration/output.mdx`:
- Line 1142: The sentence and example conflict: change either the wording or the
example so they match; for clarity update the text that currently says
“returning a plain string” to instead say “returning a plain object” when
showing the example `return { error: 'My message' }`, or replace the example
with a plain-string return like `return 'My message'` so the wording “plain
string” is accurate—ensure the doc line referencing the static message behavior
and the example are consistent.

In `@packages/zod/src/index.ts`:
- Around line 2167-2170: The mutators export currently only includes
preprocessResponse and paramsMutator, but the emitter can generate
preprocessParams, preprocessQueryParams, preprocessHeader, and preprocessBody
identifiers; update the mutators array to also conditionally include each of
preprocessParams, preprocessQueryParams, preprocessHeader, and preprocessBody
(e.g., ...(preprocessParams ? [preprocessParams] : []), etc.) so every possible
preprocess* hook emitted by the schema generator is returned and importable.
- Around line 1945-1956: The injected params context currently uses the
transformed operationName (via pascalOperationName) for operationId inside
makeParamsInjection, which is incorrect; change the operationId field to use the
original OpenAPI operationId (e.g. operation.operationId or the raw operationId
variable provided by the surrounding scope) instead of operationName so
zod.params consumers receive the real OpenAPI identifier while leaving
schemaName as `${pascalOperationName}${schemaSuffix}` and keeping paramsMutator
logic unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: abac7926-6a6c-4e38-8ad4-78e238af1f67

📥 Commits

Reviewing files that changed from the base of the PR and between 519da81 and 82a1d5d.

📒 Files selected for processing (5)
  • docs/content/docs/reference/configuration/output.mdx
  • packages/core/src/types.ts
  • packages/orval/src/utils/options.ts
  • packages/zod/src/index.ts
  • packages/zod/src/zod.test.ts

Comment thread docs/content/docs/reference/configuration/output.mdx Outdated
Comment thread packages/zod/src/index.ts
Comment thread packages/zod/src/index.ts
@titivermeesch
titivermeesch force-pushed the feat/zod-param-injection branch from 82a1d5d to f0afbe0 Compare May 28, 2026 11:18
@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

Actionable comments posted: 0

@titivermeesch
titivermeesch force-pushed the feat/zod-param-injection branch from f0afbe0 to 594e7e4 Compare May 28, 2026 11:30
@titivermeesch

Copy link
Copy Markdown
Contributor Author

Fixed relevant CodeRabbit comments

@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

Actionable comments posted: 0

@melloware melloware added the zod Zod schema client related issue label May 28, 2026
@melloware

Copy link
Copy Markdown
Collaborator

cc @wadakatu thoughts?

@melloware

melloware commented May 28, 2026

Copy link
Copy Markdown
Collaborator

@z4o4z wouldn't mind your thoughts on this too

@z4o4z z4o4z left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solid PR — the API mirrors the existing preprocess Mutator pattern, the modifier / nullary skip lists are well-targeted, and the iso.datetime / iso.time merge special-case is exactly right. Docs include a concrete example and there are 7 unit tests covering the parser-level injection.

One structural item I'd want resolved before merge (left inline near makeParamsInjection): override.zod.params is currently wired only into operation-schema generation (generateZodRoute). Component (reusable-schema) generation has no parallel call, so when paired with generateReusableSchemas: true the named component validators emit without injection — quietly missing the case where the feature compounds the most.

The other items left inline are improvements (signature length, generated-output verbosity, a fieldPath note for array/tuple/rest, missing test shapes), not blockers.

Comment thread packages/zod/src/index.ts
: undefined;

const pascalOperationName = pascal(operationName);
const makeParamsInjection = (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Major — this makeParamsInjection (and the paramsMutator setup above) is wired only into generateZodRoute. Component / reusable-schema generation — generateReusableSchemaSet in packages/orval/src/reusable-schemas.ts, plus generateZodSchemasInlineReusable / writeZodSchemasReusable in packages/orval/src/write-zod-specs.ts — has no parallel call. So when a user pairs override.zod.params with override.zod.generateReusableSchemas: true, the operation wrappers get zodParams(...) injected but the actual named component validators (Pet, Owner, …) — referenced by N operations — emit without it. That's a quiet hole exactly where the feature's value compounds.

Two reasonable resolutions:

  • Plumb a ZodParamsInjection through the reusable writer (likely with operationId: '' / a location: 'schema' sentinel so user-side zodParams can branch on it), mirroring how GenerateReusableSchemaSetOptions already threads strict / coerce / isZodV4.
  • Or scope it out explicitly in the docs (the current copy reads 'into every generated validator' without qualification).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed that this is indeed lacking. I'll make the required changes to also include the param injection in those places

Comment thread packages/zod/src/index.ts
const arrayArgs = args as ZodValidationSchemaDefinition;
const value = arrayArgs.functions
.map((prop: [string, unknown]) => parseProperty(prop))
.map((prop: [string, unknown]) => parseProperty(prop, fieldPath))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor — array (here), tuple (~L1252), and rest (~L1262) all recurse with parseProperty(prop, fieldPath) unchanged, so { tags: array<string> } and { tags: string } both yield the inner string's fieldPath: ['tags']. The validator field disambiguates the container ('array') from the element ('string'), but two tuple positions can't be distinguished, and a user-side i18n key can't tell element-level constraints from container-level ones.

Probably acceptable — it matches Zod's own issue.path, which also drops array indices — but worth a sentence in the docs alongside the existing scope explanation, or a future-proofing [] / 0 segment behind an option.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll specify this in the docs

Comment thread packages/zod/src/index.ts
strict: boolean,
isZodV4: boolean,
preprocess?: GeneratorMutator,
paramsInjection?: ZodParamsInjection,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit — paramsInjection is the 7th positional arg to parseZodValidationSchemaDefinition. The signature is already long (coerceTypes, strict, isZodV4, preprocess, paramsInjection); each new opt-in adds another. Not a blocker for this PR, but at this point an options-object overload would age better and let future fields land without breaking call sites.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't touch this for now as it will increase the complexity of this PR beyond it's original scope, but happy to pick it up later on

Comment thread packages/zod/src/index.ts

const formattedArgs = formatFunctionArgs(args);
const paramsArg = buildParamsArg(fn, fieldPath);
let combinedArgs: string;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two small nits:

  1. buildParamsArg (above, ~L1037–1049) inlines JSON.stringify(ctx) verbatim at every call site, so for a 10-field schema with three constraints each the same operationId / location / schemaName prefix is repeated 30+ times in the emitted file. Functionally fine, but a follow-up that hoists a per-property const (const _ctx_email = { … }) would shrink output noticeably.

  2. The combinedArgs branching here is correct but a small helper (combineParamsArg(formattedArgs, paramsArg, fn) returning the merged string) would localize the PARAMS_MERGE_INTO_OPTIONS_VALIDATORS special-case knowledge and let the .coerce.${fn}(...) and .${fn}(...) returns stay symmetric.

expect(zod).not.toContain('zodParams(');
expect(zod).toContain('zod.string().email()');
});
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage is good for the parser-level injection, but a few realistic shapes aren't exercised:

  • No end-to-end test through generateZodRoute — the new wiring (the makeParamsInjection('body', 'Body') family, the [...preprocess, paramsMutator] aggregation in mutators, the ${pascalOperationName}${schemaSuffix} derivation) is all untested.
  • No reusable-schema test (which would surface the structural gap I flagged on index.ts:1946).
  • No tuple / rest / additionalProperties test — only array and union exercise structural fieldPath propagation.
  • No regex / length / multipleOf test — the docs claim these are in scope but only min / max are checked.

@wadakatu wadakatu left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the ping. Overall this fills a real gap — generator-time context (operationId / schemaName / fieldPath) is exactly what Zod's global error map can't disambiguate, the preprocess-style Mutator wiring keeps it consistent, and it's non-breaking when unset. +1 to @z4o4z's reusable-schema point as the thing to resolve before merge. A few smaller notes inline.

```ts title="zod-params.ts"
import { i18n } from './i18n';

type ZodParamsContext = {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this context type be exported from orval instead of hand-written here? Everywhere else orval hands a user function an orval-specific object, the shape ships as an exported type (e.g. override.operationNameOpenApiOperationObject / Verbs via @orval/core). preprocess is the exception only because its signature is plain Zod. Exporting a ZodParamsContext would give users completion + surface breakage when the context evolves, and let this doc import type it instead of duplicating the shape.

- Applied to base types (`string`, `number`, `boolean`, `bigint`, `date`, `integer`), constraints (`min`, `max`, `gt`, `lt`, `multipleOf`, `regex`, `length`), formats (`email`, `url`, `uuid`, `hostname`, `datetime`, `time`), and `literal`, `enum`, `instanceof`, `stringFormat`.
- Skipped on modifiers (`optional`, `nullable`, `nullish`, `default`, `describe`) and structural calls (`object`, `array`, `tuple`, `union`, `rest`, `passthrough`, `strict`).

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The { error } shape is zod v4-only. orval supports both v3 and v4 and this injection runs on both paths, but I checked against zod 3.25.76 / 4.3.6: on v3 { error } is silently ignored — no build- or parse-time error, the message just stays the default (.email({ error: 'x' })"Invalid email"). { message } works on both. Might be worth a one-liner noting the v3/v4 difference and suggesting { message } for anyone supporting both versions.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a section to the docs to specify this

* schema name, field path, validator name) and returns a Zod `params` object
* (e.g. `{ error: ... }`) that is appended as the trailing argument.
*/
params?: Mutator;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tiny naming flag (defer to you): generate / coerce / preprocess all key the path-parameter location as param (singular), and this context's location can be 'param' too. The new params (plural) next to them leans on singular-vs-plural to separate two unrelated concepts. Keeping params is defensible since it's Zod's term for the validator's 2nd argument — just flagging in case validatorParams reads cleaner.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clarified in the docs as I would like to keep the zod semantics here

@titivermeesch
titivermeesch force-pushed the feat/zod-param-injection branch from 594e7e4 to 1251dba Compare May 29, 2026 08:03

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
packages/orval/src/write-zod-specs.ts (2)

311-326: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Inline non-reusable schemas still drop the new params mutator.

Line 321 forwards paramsMutator only through the reusable branch. In the regular inline path, parseZodValidationSchemaDefinition(...) still runs without params context, so override.zod.params silently does nothing unless generateReusableSchemas is on.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/orval/src/write-zod-specs.ts` around lines 311 - 326, The
non-reusable inline path in generateZodSchemasInline is not forwarding the
paramsMutator, so override.zod.params is ignored unless generateReusableSchemas
is true; fix by passing the paramsMutator into the non-reusable generation flow
(the same way generateZodSchemasInlineReusable receives it) and ensure
parseZodValidationSchemaDefinition (and any callers that build parameter
schemas) are invoked with that paramsMutator/context so override.zod.params is
honored for inline schemas as well.

435-453: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

The new mutator is gated to reusable-schema mode here.

Lines 445-453 pass paramsMutator only into writeZodSchemasReusable. When users generate standalone zod schema files without generateReusableSchemas, the regular branch below still emits validators without the injected params, so the option is only partially implemented.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/orval/src/write-zod-specs.ts` around lines 435 - 453, In
writeZodSchemas, the paramsMutator is only forwarded into
writeZodSchemasReusable when output.override.zod.generateReusableSchemas is
true, leaving the non-reusable path without the injected mutator; forward
paramsMutator into the other code path as well (the branch that emits
standalone/regular schema validators) so both branches use the mutator, and
ensure any helper that generates validators (the same emitter used by
writeZodSchemasReusable or the local emission function inside writeZodSchemas)
accepts and uses paramsMutator when generating validators.
packages/orval/src/write-specs.ts (1)

349-370: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Thread override.zod.params into verb schema files too.

Lines 349-370 only wire the new mutator into writeZodSchemas, but the sibling writeZodSchemasFromVerbs path still generates *Body / *Params / *Headers / response schemas without any params injection. That leaves output.schemas mode inconsistent with the PR contract of applying params to each generated validator.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/orval/src/write-specs.ts` around lines 349 - 370, The verb-path
generation path is missing the params mutator: pass the same
schemasParamsMutator used for writeZodSchemas into writeZodSchemasFromVerbs and
its implementation so verb-generated validators
(*Body/*Params/*Headers/response) also apply override.zod.params; update the
call site (where writeZodSchemasFromVerbs is invoked with builder.verbOptions,
schemasPath, fileExtension, header, output, {...}) to include
schemasParamsMutator, and update the writeZodSchemasFromVerbs signature and any
internal uses to accept and invoke the mutator when building schemas from
builder.verbOptions and builder.spec so output.schemas mode is consistent with
writeZodSchemas.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@packages/orval/src/write-specs.ts`:
- Around line 349-370: The verb-path generation path is missing the params
mutator: pass the same schemasParamsMutator used for writeZodSchemas into
writeZodSchemasFromVerbs and its implementation so verb-generated validators
(*Body/*Params/*Headers/response) also apply override.zod.params; update the
call site (where writeZodSchemasFromVerbs is invoked with builder.verbOptions,
schemasPath, fileExtension, header, output, {...}) to include
schemasParamsMutator, and update the writeZodSchemasFromVerbs signature and any
internal uses to accept and invoke the mutator when building schemas from
builder.verbOptions and builder.spec so output.schemas mode is consistent with
writeZodSchemas.

In `@packages/orval/src/write-zod-specs.ts`:
- Around line 311-326: The non-reusable inline path in generateZodSchemasInline
is not forwarding the paramsMutator, so override.zod.params is ignored unless
generateReusableSchemas is true; fix by passing the paramsMutator into the
non-reusable generation flow (the same way generateZodSchemasInlineReusable
receives it) and ensure parseZodValidationSchemaDefinition (and any callers that
build parameter schemas) are invoked with that paramsMutator/context so
override.zod.params is honored for inline schemas as well.
- Around line 435-453: In writeZodSchemas, the paramsMutator is only forwarded
into writeZodSchemasReusable when output.override.zod.generateReusableSchemas is
true, leaving the non-reusable path without the injected mutator; forward
paramsMutator into the other code path as well (the branch that emits
standalone/regular schema validators) so both branches use the mutator, and
ensure any helper that generates validators (the same emitter used by
writeZodSchemasReusable or the local emission function inside writeZodSchemas)
accepts and uses paramsMutator when generating validators.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e5ed0316-c0e0-4d2e-883b-9fa16982680d

📥 Commits

Reviewing files that changed from the base of the PR and between 594e7e4 and 1251dba.

📒 Files selected for processing (10)
  • docs/content/docs/reference/configuration/output.mdx
  • packages/core/src/types.ts
  • packages/orval/src/index.ts
  • packages/orval/src/reusable-schemas.test.ts
  • packages/orval/src/reusable-schemas.ts
  • packages/orval/src/utils/options.ts
  • packages/orval/src/write-specs.ts
  • packages/orval/src/write-zod-specs.ts
  • packages/zod/src/index.ts
  • packages/zod/src/zod.test.ts
✅ Files skipped from review due to trivial changes (1)
  • docs/content/docs/reference/configuration/output.mdx
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/core/src/types.ts
  • packages/orval/src/utils/options.ts
  • packages/zod/src/index.ts

@titivermeesch
titivermeesch force-pushed the feat/zod-param-injection branch from 1251dba to dc85d81 Compare May 29, 2026 08:12
@titivermeesch

Copy link
Copy Markdown
Contributor Author

@wadakatu @z4o4z I believe I addressed all your comments. Do you mind looking at it again?

@titivermeesch
titivermeesch requested review from wadakatu and z4o4z May 29, 2026 08:14

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/orval/src/write-zod-specs.ts (1)

612-622: 💤 Low value

Minor: string inclusion check could false-positive on substring matches.

The check entry.zod.includes(paramsMutator.name) may incorrectly flag the import as needed if the mutator name is a substring of another identifier (e.g., a mutator named min would match .min(1)). Since users typically use distinctive names like zodParams and a false positive only adds an unused import, this is acceptable — but a regex word boundary check (new RegExp(\\b${name}\b`)`) would be more precise if this causes issues.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/orval/src/write-zod-specs.ts` around lines 612 - 622, The current
check for whether to include the params mutator import uses a substring test
(entry.zod.includes(paramsMutator.name)) which can false-positive on partial
matches; update the logic that computes needsParamsImport (used with
paramsMutator, paramsMutator.name, entry.zod, and paramsMutatorImport) to test
for a whole-word match instead (e.g., build a RegExp with word boundaries around
paramsMutator.name and test entry.zod against it) so the import is only emitted
when the exact identifier appears.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/orval/src/write-zod-specs.ts`:
- Around line 612-622: The current check for whether to include the params
mutator import uses a substring test (entry.zod.includes(paramsMutator.name))
which can false-positive on partial matches; update the logic that computes
needsParamsImport (used with paramsMutator, paramsMutator.name, entry.zod, and
paramsMutatorImport) to test for a whole-word match instead (e.g., build a
RegExp with word boundaries around paramsMutator.name and test entry.zod against
it) so the import is only emitted when the exact identifier appears.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6e98f46c-078e-4b65-be09-f0fd7669a0b4

📥 Commits

Reviewing files that changed from the base of the PR and between 1251dba and dc85d81.

📒 Files selected for processing (10)
  • docs/content/docs/reference/configuration/output.mdx
  • packages/core/src/types.ts
  • packages/orval/src/index.ts
  • packages/orval/src/reusable-schemas.test.ts
  • packages/orval/src/reusable-schemas.ts
  • packages/orval/src/utils/options.ts
  • packages/orval/src/write-specs.ts
  • packages/orval/src/write-zod-specs.ts
  • packages/zod/src/index.ts
  • packages/zod/src/zod.test.ts
✅ Files skipped from review due to trivial changes (2)
  • packages/orval/src/index.ts
  • docs/content/docs/reference/configuration/output.mdx
🚧 Files skipped from review as they are similar to previous changes (6)
  • packages/core/src/types.ts
  • packages/orval/src/utils/options.ts
  • packages/orval/src/write-specs.ts
  • packages/orval/src/reusable-schemas.test.ts
  • packages/orval/src/reusable-schemas.ts
  • packages/zod/src/zod.test.ts

@titivermeesch
titivermeesch force-pushed the feat/zod-param-injection branch from dc85d81 to a6e9a22 Compare May 29, 2026 08:34

@z4o4z z4o4z left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review against a6e9a223. The previous round is well-handled — thanks for the thorough follow-up.

Prior comments — all addressed ✅

  • The Major one (injection only in generateZodRoute, missing component schemas) is wired through reusable-schemas.ts + write-specs.ts for separate-files mode and covered by two new reusable-schemas.test.ts cases.
  • array/tuple/rest fieldPath semantics are now documented in output.mdx.
  • The end-to-end test gap is closed — 11 parser cases plus a real generateZod e2e test. Nice.
  • Bonus: CodeRabbit's operationId-vs-operationName Major is genuinely fixed (the e2e test asserts the raw createCat), and paramsMutator is now in the returned mutators array.

One new finding — Major: inline-reusable mode silently skips injection.

The component-schema injection only fires in separate-files mode. In inline-reusable mode it's dead-wired:

  • shouldGenerateZodSchemasInline (write-specs.ts:288) returns true for the common config client: 'zod' + generateReusableSchemas: true with no output.schemas.
  • That path calls generateZodSchemasInline(builder, output, includeZodImport) at write-specs.ts:489 with no 4th arg, so paramsMutator is undefined all the way into generateZodSchemasInlineReusablegenerateReusableSchemaSet({ paramsMutator: undefined }) → no params on the inlined component schemas.
  • Net: in inline-reusable mode the operation request/response schemas get zodParams(...) (via generateZodRoute) but the shared export const Pet = … definitions they reference do not — whereas separate-files mode injects both. The documented option behaves differently depending on output mode.

Small fix: hoist the schemasParamsMutator resolution out of the isZodSchemas block and pass it at :489:

generateSchemasInline: needZodSchemasInline
  ? () => generateZodSchemasInline(builder, output, includeZodImport, schemasParamsMutator)
  : undefined,

Or, if inline-mode injection is intentionally out of scope, drop the dead param + comment and note the limitation in the docs — right now it reads as supported. See the two inline notes.

Recommendation: tests and prior feedback are solid; the inline-reusable gap is the one thing I'd want resolved (or explicitly documented) before merge — it's a real per-mode inconsistency for a mainstream config, and it ships with a comment pointing at a function that doesn't exist.

Comment thread packages/orval/src/write-zod-specs.ts Outdated
const prefix = includeZodImport ? `import { z as zod } from 'zod';\n\n` : '';
// The `zodParams` import is registered as a mutator on the operation file
// (via `generateZodRoute`) and on the standalone single-mode wrapper (via
// `getInlineZodSchemasMutators`), so the import line is emitted by the

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment references getInlineZodSchemasMutators, but that function doesn't exist anywhere in the repo — so the mechanism it describes ("the import line is emitted by the outer file builder") was never implemented for the inline path.

Concretely: the only caller of generateZodSchemasInline (write-specs.ts:489) passes just 3 args, so paramsMutator is always undefined here in inline-reusable mode. Component schemas inlined via this function therefore get no zodParams(...) injection — even though operations in the same file do (via generateZodRoute). Separate-files mode (writeZodSchemasReusable) injects correctly, so behavior diverges by output mode.

Either wire the mutator through at :489 (hoist schemasParamsMutator so both branches share it) or, if inline injection is out of scope, remove the dead param and this comment and document the limitation.

builder: WriteZodSchemasInput,
output: WriteZodOutputOptions,
includeZodImport = true,
paramsMutator?: GeneratorMutator,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dead parameter: generateZodSchemasInline's only production caller (write-specs.ts:489) never passes a 4th arg, so paramsMutator is always undefined and the value threaded into generateZodSchemasInlineReusablegenerateReusableSchemaSet is too. The plumbing is in place but nothing feeds it. Pass schemasParamsMutator from write-specs.ts to close the inline-reusable gap (see the comment below at line 442).

@titivermeesch
titivermeesch force-pushed the feat/zod-param-injection branch from a6e9a22 to c92f532 Compare May 29, 2026 13:53
@titivermeesch

Copy link
Copy Markdown
Contributor Author

@z4o4z I believe I addressed your concerns

@melloware

Copy link
Copy Markdown
Collaborator

@titivermeesch sorry just merged some stuff and you have 1 merge conflict!

@titivermeesch

Copy link
Copy Markdown
Contributor Author

Probably conflicting with my own changes. Looking into it

@titivermeesch
titivermeesch force-pushed the feat/zod-param-injection branch from c92f532 to 9a1ba2d Compare May 29, 2026 14:07
@titivermeesch

Copy link
Copy Markdown
Contributor Author

@melloware fixed the conflicts

@melloware
melloware merged commit 88ad4a2 into orval-labs:master May 29, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

zod Zod schema client related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow injecting custom params into generated Zod schemas IL8N

4 participants