Skip to content

feat(query): add useFlatInput option to merge params into a single object#3167

Draft
inas-sirhan wants to merge 10 commits into
orval-labs:masterfrom
inas-sirhan:feat/flatten-parameters
Draft

feat(query): add useFlatInput option to merge params into a single object#3167
inas-sirhan wants to merge 10 commits into
orval-labs:masterfrom
inas-sirhan:feat/flatten-parameters

Conversation

@inas-sirhan
Copy link
Copy Markdown
Contributor

@inas-sirhan inas-sirhan commented Mar 30, 2026

Summary

Adds useFlatInput?: boolean config option under override that flattens path params, query params, and body into a single object argument for query hooks, query options functions, and mutations.

Example

// orval.config.ts
override: {
  useFlatInput: true,
}

Query (path + query params):

// Before
useGetUserOrders(userId, params, options)

// After
useGetUserOrders({ userId, status, fromDate }, options)

Mutation (query params + body):

// Before
mutate({ data: { name: 'Rex' }, params: { limit: 10 } })

// After
mutate({ name: 'Rex', limit: 10 })

Mutation (path + query + body):

// Before
mutate({ data: { query: 'search' }, params: { page: 1, limit: 10 } })

// After
mutate({ orgId: '123', page: 1, limit: 10, query: 'search' })

How it works

  • Uses TypeScript intersection types (e.g. { userId: number } & GetUserOrdersParams)
  • Path params + query param fields destructured by name, body as rest spread
  • Throws on duplicate field names across path/query params
  • Throws if used together with useNamedParameters (they conflict)
  • Headers stay as a separate parameter
  • Existing behavior unchanged when option is not set

Tests

Snapshot tests for:

  • Petstore (mutations with query + body, headers)
  • Multi-params (queries with path + query params)
  • Path + query + body (all three combined in a mutation)

Closes #3164

Summary by CodeRabbit

  • New Features

    • Opt-in "flat input" mode for query/mutation hooks and option builders — pass path, query and body params as a single flattened object.
    • Generator now exposes query field names to support flat input and updated config entries to enable it.
  • Bug Fixes

    • Runtime validation that errors on duplicate parameter names when flat input is enabled.
  • Tests / Examples

    • Added generated React Query examples, models, specs and configs demonstrating flat-input scenarios.

…ject

- Add useFlatInput?: boolean config under override
- Queries: destructure path params by name, rest spread for query params
- Mutations: destructure path + query param fields by name, rest spread for body
- Uses TypeScript intersection types (e.g. { userId: number } & GetUserOrdersParams)
- Throws error on duplicate field names across path/query params
- Throws error if used together with useNamedParameters
- Headers stay as a separate parameter
- Snapshot tests for petstore, multi-params, and path+query+body cases

Closes orval-labs#3160
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 30, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: df242e95-dec1-418c-b423-9d6700ed2078

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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 an optional flat-input generation mode (override.useFlatInput) that flattens path/query/body params into one input shape, threads a flattened variable type through generators and adapters, emits flattened hook/mutation signatures and call sites, and adds generation-time collision detection for combined names.

Changes

Cohort / File(s) Summary
Core getters & types
packages/core/src/getters/query-params.ts, packages/core/src/types.ts
Added fieldNames?: string[] to GetterQueryParam and useFlatInput?: boolean to OverrideOutput / NormalizedOverrideOutput.
Flat-input validation
packages/query/src/flat-input.ts
New exported checkFlatInputCollisions(operationName, props, queryParams?) detects duplicate names across path/query/body when useFlatInput is used and throws a descriptive error.
Query generator
packages/query/src/query-generator.ts
Wire useFlatInput into generation: classify props, emit flattened/destructured overloads/implementations, validate incompatibility with useNamedParameters, and pass useFlatInput into implementation generation.
Mutation generator
packages/query/src/mutation-generator.ts
Invoke collision checker when useFlatInput enabled; generate flattened intersection input types, adjusted call-arg construction, and thread override variable/generic typing into generated mutation signatures.
Framework adapter & frameworks
packages/query/src/framework-adapter.ts, packages/query/src/frameworks/...
Extended adapter API to accept optional overrideVariableType?: string and forwarded it through Angular, Svelte, and index adapters to influence generated options/argument types.
Query options helper
packages/query/src/query-options.ts
getQueryOptionsDefinition accepts overrideVariableType?: string and prefers it for the mutation variable generic when provided.
Generated snapshots — endpoints
tests/__snapshots__/react-query/flat-input-*/endpoints.ts
Added multiple React Query endpoint snapshot modules demonstrating flat-input hooks/mutations across scenarios (multi-params, path+query+body, petstore).
Generated snapshots — models
tests/__snapshots__/react-query/flat-input-*/model/*
Added many generated model/type snapshot files (params, enums, bodies, composed models) supporting the flat-input endpoint snapshots.
Test configs & specs
tests/configs/react-query.config.ts, tests/specifications/flat-input-with-body-query.yaml
Added three react-query generation configs enabling override.useFlatInput: true and a new OpenAPI spec (POST /api/search/{orgId}) used by snapshots.

Sequence Diagram(s)

sequenceDiagram
  participant Gen as Generator
  participant Val as FlatInputChecker
  participant Adapter as FrameworkAdapter
  participant Runtime as GeneratedHook/Mutation

  Gen->>Val: collect prop names + queryParams.fieldNames
  Val-->>Gen: return OK / throw duplicate-name Error
  Gen->>Adapter: emit definitions (include overrideVariableType when flat)
  Adapter->>Runtime: generate hook/mutation args & types (uses overrideVariableType)
  Runtime->>Runtime: runtime call uses flattened args shape when enabled
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Possibly related PRs

Suggested labels

tanstack-query

Suggested reviewers

  • melloware
  • snebjorn

Poem

🐇
I hopped through props both wide and deep,
I counted names that wake or sleep,
Paths, queries, bodies joined as one,
I sniffed for clashes, raised the sun,
Flat inputs now roll — carrots for fun! 🥕

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The linked issue #3160 is about Zod branded types, which is completely unrelated to the useFlatInput feature being implemented in this PR. Verify that #3160 is the correct linked issue. If not, link the actual issue this PR addresses (likely #3167 or the original issue that requested useFlatInput).
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately describes the main feature: adding a useFlatInput option that merges path params, query params, and body into a single flattened object for query hooks and mutations.
Out of Scope Changes check ✅ Passed All changes are focused on implementing the useFlatInput feature for flattening parameters across queries and mutations, with appropriate test snapshots and configuration updates.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ 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.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 10

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/query/src/flat-input.ts`:
- Around line 20-29: The duplicate-name check currently builds allNames from
pathParamNames and queryFieldNames only, so body fields are not validated;
update useFlatInput to also collect body field names (e.g.,
requestBody.fieldNames or derive bodyFieldNames from the operation's requestBody
schema) and include them in allNames (const allNames = [...pathParamNames,
...queryFieldNames, ...bodyFieldNames]); keep using the existing seen Set and
throw the same Error (including operationName) when a duplicate is found so
path, query, and body field name collisions are rejected consistently.

In `@packages/query/src/mutation-generator.ts`:
- Around line 127-155: The generated flat-input type string stored in
definitions is sometimes already an intersection like "{ orgId: string } &
SearchItemsParams & SearchItemsBody" but later code wraps it again with
"{${definitions}}", producing "{{ ... }}" invalid TS; update the flat-input
branch in mutation-generator.ts (the code using useFlatInput, definitions and
flatMutationCallArgs) to detect when definitions is already an
object/intersection (e.g., startsWith('{') or contains '&') and in that case
pass definitions directly instead of wrapping it in another "{...}"; keep the
existing behavior for single plain types so single-type cases still get wrapped
as an object when needed.

In `@packages/query/src/query-generator.ts`:
- Around line 388-393: The current hasMultiplePropTypes check only counts
categories and fails for cases like multiple path params; change it to count
actual exposed props instead of categories. Replace the boolean expression for
hasMultiplePropTypes with a totalProps calculation that sums pathParams.length
plus the number of props in queryParamProp (if present) plus the number of props
in bodyProp (if present), then set hasMultiplePropTypes = totalProps > 1 so
useFlatInput will flatten when there are multiple exposed inputs (mirror the
mutation generator's props.length > 1 behavior).
- Around line 413-426: The new queryOptionsFn signature was changed to a single
flattened object (useFlatInput / flatInputDestructure) but callers still build
and pass the old split props (queryProps) into generatePrefetch and into
queryProperties/httpFunctionProps, causing mismatched args; update the code so
that when useFlatInput is true you derive queryProperties/httpFunctionProps from
the flattened shape (flatProps / flatInputDestructure) or simply pass flatProps
into generatePrefetch and any caller that previously used queryProps, ensuring
queryProperties, httpFunctionProps and generatePrefetch all consume the same
flat shape as queryOptionsFn.

In `@tests/__snapshots__/react-query/flat-input-multi-params/endpoints.ts`:
- Around line 88-89: useGetUsersUserIdOrders is calling
getGetUsersUserIdOrdersQueryOptions with positional args (userId, params,
options) but the helper's signature expects a single flat object ({ userId,
...params }) and optional options; update the call site in
useGetUsersUserIdOrders to pass one object merging userId and params (e.g., {
userId, ...params }) as the first argument and pass options as the second
argument so the query key is built from the correct shape for
getGetUsersUserIdOrdersQueryOptions.
- Around line 80-85: The query key builder getGetUsersUserIdOrdersQueryKey
currently treats an empty params object as present and appends it to the key,
causing cache forks; update the function to only append params when they are
non-null/undefined AND not an empty object (e.g., check params &&
Object.keys(params).length > 0) so getGetUsersUserIdOrdersQueryKey(userId) and
getGetUsersUserIdOrdersQueryKey(userId, {}) produce the same key; apply the same
empty-object guard to the other similar query key builders in this diff.

In `@tests/__snapshots__/react-query/flat-input-path-query-body/endpoints.ts`:
- Around line 77-79: The emitted mutation types and call order are wrong: remove
the stray double braces so the generic target type becomes "{ orgId: string } &
SearchItemsParams & SearchItemsBody" (fix both getSearchItemsMutationOptions and
useSearchItems) and correct the mutationFn invocation to call searchItems(orgId,
data, { page, limit }, fetchOptions) to match the function signature
(searchItems(orgId, searchItemsBody, params?, options?)). Ensure mutationFn
references the same parameter names used in the file (e.g., orgId, data, page,
limit, fetchOptions) and update both occurrences noted
(getSearchItemsMutationOptions, useSearchItems, and the mutationFn block).

In
`@tests/__snapshots__/react-query/flat-input-path-query-body/model/searchItemsParams.ts`:
- Around line 8-11: The exported type alias SearchItemsParams has its properties
(page, limit) unindented; update the generator/pretty-printer that renders
exported type aliases so that properties inside a type block are prefixed with
two spaces like other generated types (ensure the rendering branch that outputs
"export type <Name> = {" indents subsequent property lines and closing "};");
locate the code that formats/renderTypeAlias or the snapshot emitter for the
react-query flat-input-path-query-body model and adjust the indentation logic so
page and limit are emitted with two-space indentation consistent with other
types.

In `@tests/__snapshots__/react-query/flat-input-petstore/endpoints.ts`:
- Around line 244-246: The generated TypeScript has three issues: the variable
type is emitted as "{CreatePetsParams & CreatePetsBody}" (invalid — remove the
braces), the required CreatePetsHeaders parameter is missing from
getCreatePetsMutationOptions and useCreatePets (add a headers?:
CreatePetsHeaders parameter and thread it through), and the createPets call uses
the wrong argument order; change calls from createPets({ limit, sort }, data,
fetchOptions) to createPets(data, { limit, sort }, headers, fetchOptions).
Update the UseMutationOptions generic type to use CreatePetsParams &
CreatePetsBody (without braces) and apply the same fixes to the other
occurrences noted (around the other get*/use* functions at the referenced
blocks).

In `@tests/configs/react-query.config.ts`:
- Around line 776-823: In the React Query configs flatInputPetstore,
flatInputMultiParams, and flatInputPathQueryBody the output option uses the
unsupported key prettier: true; replace each occurrence with formatter:
'prettier' so Orval recognizes and applies formatting—update the output objects
inside flatInputPetstore, flatInputMultiParams, and flatInputPathQueryBody to
use formatter: 'prettier' instead of prettier: true.
🪄 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: 9ed130db-738e-4f22-b6f6-0be9b94508a9

📥 Commits

Reviewing files that changed from the base of the PR and between 9483f4c and bbbb4a5.

📒 Files selected for processing (43)
  • packages/core/src/getters/query-params.ts
  • packages/core/src/types.ts
  • packages/query/src/flat-input.ts
  • packages/query/src/mutation-generator.ts
  • packages/query/src/query-generator.ts
  • tests/__snapshots__/react-query/flat-input-multi-params/endpoints.ts
  • tests/__snapshots__/react-query/flat-input-multi-params/model/getUsersUserIdOrdersParams.ts
  • tests/__snapshots__/react-query/flat-input-multi-params/model/getUsersUserIdOrdersStatus.ts
  • tests/__snapshots__/react-query/flat-input-multi-params/model/index.ts
  • tests/__snapshots__/react-query/flat-input-path-query-body/endpoints.ts
  • tests/__snapshots__/react-query/flat-input-path-query-body/model/index.ts
  • tests/__snapshots__/react-query/flat-input-path-query-body/model/searchItems200.ts
  • tests/__snapshots__/react-query/flat-input-path-query-body/model/searchItems200ResultsItem.ts
  • tests/__snapshots__/react-query/flat-input-path-query-body/model/searchItemsBody.ts
  • tests/__snapshots__/react-query/flat-input-path-query-body/model/searchItemsBodyFilters.ts
  • tests/__snapshots__/react-query/flat-input-path-query-body/model/searchItemsParams.ts
  • tests/__snapshots__/react-query/flat-input-petstore/endpoints.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/cat.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/catType.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/createPetsBody.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/createPetsHeaders.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/createPetsParams.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/createPetsSort.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/createPetsXExample.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/dachshund.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/dachshundBreed.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/dog.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/dogType.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/error.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/index.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/labradoodle.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/labradoodleBreed.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/listPetsHeaders.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/listPetsParams.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/listPetsSort.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/listPetsXExample.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/pet.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/petCallingCode.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/petCountry.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/petWithTag.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/pets.ts
  • tests/configs/react-query.config.ts
  • tests/specifications/flat-input-with-body-query.yaml

Comment on lines +20 to +29
const queryFieldNames = queryParams?.fieldNames ?? [];

const allNames = [...pathParamNames, ...queryFieldNames];
const seen = new Set<string>();

for (const name of allNames) {
if (seen.has(name)) {
throw new Error(
`useFlatInput: duplicate parameter name "${name}" found in operation "${operationName}". ` +
`Path params, query params, and body fields must have unique names when using useFlatInput.`,
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.

⚠️ Potential issue | 🟠 Major

Also validate request-body field names here.

allNames only contains path and query names. If the body also exposes page, limit, or orgId, flat-input generation will silently pull that field out of ...data instead of rejecting the operation, even though this helper says body collisions are covered.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/query/src/flat-input.ts` around lines 20 - 29, The duplicate-name
check currently builds allNames from pathParamNames and queryFieldNames only, so
body fields are not validated; update useFlatInput to also collect body field
names (e.g., requestBody.fieldNames or derive bodyFieldNames from the
operation's requestBody schema) and include them in allNames (const allNames =
[...pathParamNames, ...queryFieldNames, ...bodyFieldNames]); keep using the
existing seen Set and throw the same Error (including operationName) when a
duplicate is found so path, query, and body field name collisions are rejected
consistently.

Comment thread packages/query/src/mutation-generator.ts Outdated
Comment thread packages/query/src/query-generator.ts Outdated
Comment on lines +388 to +393
const hasMultiplePropTypes =
(pathParams.length > 0 ? 1 : 0) +
(queryParamProp ? 1 : 0) +
(bodyProp ? 1 : 0) >
1;

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.

⚠️ Potential issue | 🟠 Major

useFlatInput still won't flatten queries with only path params.

This check only counts prop categories, not actual exposed inputs. A GET with two path params still evaluates to one category, so queries keep positional args even though the mutation generator flattens the same shape with props.length > 1.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/query/src/query-generator.ts` around lines 388 - 393, The current
hasMultiplePropTypes check only counts categories and fails for cases like
multiple path params; change it to count actual exposed props instead of
categories. Replace the boolean expression for hasMultiplePropTypes with a
totalProps calculation that sums pathParams.length plus the number of props in
queryParamProp (if present) plus the number of props in bodyProp (if present),
then set hasMultiplePropTypes = totalProps > 1 so useFlatInput will flatten when
there are multiple exposed inputs (mirror the mutation generator's props.length
> 1 behavior).

Comment thread packages/query/src/query-generator.ts Outdated
Comment on lines +413 to +426
if (queryParamProp && bodyProp) {
const knownNames = [...pathNames, ...queryFieldNames];
flatInputDestructure = `{ ${[...knownNames, `...${bodyProp.name}`].join(', ')} }: ${flatInputType},`;
} else if (queryParamProp) {
flatInputDestructure = `{ ${[...pathNames, '...params'].join(', ')} }: ${flatInputType},`;
} else if (bodyProp) {
flatInputDestructure = `{ ${[...pathNames, `...${bodyProp.name}`].join(', ')} }: ${flatInputType},`;
}
}

const flatProps =
useFlatInput && flatInputDestructure ? flatInputDestructure : queryProps;

const queryOptionsFn = `export const ${queryOptionsFnName} = <TData = ${TData}, TError = ${errorType}>(${httpFirstParam}${flatProps} ${queryArgumentsForOptions}) => {
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.

⚠️ Potential issue | 🔴 Critical

The new flat queryOptionsFn signature doesn't match its existing callers.

This branch changes queryOptionsFn to accept a single flattened object, but queryProperties / httpFunctionProps were already derived from the pre-flat prop shape, and Lines 498-506 still feed queryProps into generatePrefetch(). The new searchItems path+query+body case is exactly this shape, so the emitted query options/hooks keep calling the old split signature with missing or positional args.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/query/src/query-generator.ts` around lines 413 - 426, The new
queryOptionsFn signature was changed to a single flattened object (useFlatInput
/ flatInputDestructure) but callers still build and pass the old split props
(queryProps) into generatePrefetch and into queryProperties/httpFunctionProps,
causing mismatched args; update the code so that when useFlatInput is true you
derive queryProperties/httpFunctionProps from the flattened shape (flatProps /
flatInputDestructure) or simply pass flatProps into generatePrefetch and any
caller that previously used queryProps, ensuring queryProperties,
httpFunctionProps and generatePrefetch all consume the same flat shape as
queryOptionsFn.

Comment on lines +80 to +85
export const getGetUsersUserIdOrdersQueryKey = (userId: number,
params?: GetUsersUserIdOrdersParams,) => {
return [
`/users/${userId}/orders`, ...(params ? [params] : [])
] as const;
}
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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Expect different outputs here; the second key shows the extra empty object
# produced by the current flat-input rest pattern.
node - <<'NODE'
const getKey = (userId, params) => [`/users/${userId}/orders`, ...(params ? [params] : [])];
const fromFlatInput = ({ userId, ...params }) => getKey(userId, params);

console.log(JSON.stringify(getKey(1)));
console.log(JSON.stringify(fromFlatInput({ userId: 1 })));
NODE

Repository: orval-labs/orval

Length of output: 102


🏁 Script executed:

cd /repo && find . -name "endpoints.ts" -path "*react-query/flat-input-multi-params*" | head -5

Repository: orval-labs/orval

Length of output: 115


🏁 Script executed:

cd /repo && head -100 tests/__snapshots__/react-query/flat-input-multi-params/endpoints.ts | cat -n

Repository: orval-labs/orval

Length of output: 115


🏁 Script executed:

cd /repo && sed -n '50,120p' tests/__snapshots__/react-query/flat-input-multi-params/endpoints.ts | cat -n

Repository: orval-labs/orval

Length of output: 115


🏁 Script executed:

pwd && ls -la | head -20

Repository: orval-labs/orval

Length of output: 1295


🏁 Script executed:

find . -name "endpoints.ts" -path "*flat-input-multi-params*" 2>/dev/null | head -3

Repository: orval-labs/orval

Length of output: 130


🏁 Script executed:

cat -n tests/__snapshots__/react-query/flat-input-multi-params/endpoints.ts | sed -n '1,120p'

Repository: orval-labs/orval

Length of output: 4825


🏁 Script executed:

grep -n "getGetUsersUserIdOrders" tests/__snapshots__/react-query/flat-input-multi-params/endpoints.ts | head -20

Repository: orval-labs/orval

Length of output: 777


🏁 Script executed:

sed -n '88,104p' tests/__snapshots__/react-query/flat-input-multi-params/endpoints.ts | cat -n

Repository: orval-labs/orval

Length of output: 1058


🏁 Script executed:

# Check if there are other snapshot files with similar patterns
find tests/__snapshots__/react-query -name "endpoints.ts" | wc -l

Repository: orval-labs/orval

Length of output: 62


🏁 Script executed:

# Compare with non-flat-input version to see the difference
find tests/__snapshots__/react-query -name "endpoints.ts" -not -path "*flat-input*" | head -1 | xargs sed -n '80,95p' | cat -n

Repository: orval-labs/orval

Length of output: 441


🏁 Script executed:

# Find a non-flat-input endpoint with similar multi-params pattern
find tests/__snapshots__/react-query -name "endpoints.ts" -not -path "*flat-input*" -type f | head -3

Repository: orval-labs/orval

Length of output: 234


🏁 Script executed:

# Look at a non-flat-input version if it exists
find tests/__snapshots__/react-query -type d -name "*multi-params*" | grep -v flat-input

Repository: orval-labs/orval

Length of output: 123


🏁 Script executed:

# Check the source code that generates these snapshots to understand the intentional behavior
find . -name "*query-generator*" -o -name "*flat*" | grep -E "\.(ts|tsx)$" | head -10

Repository: orval-labs/orval

Length of output: 134


🏁 Script executed:

# Look at the actual source generator
find packages -name "*.ts" | xargs grep -l "getGetUsersUserIdOrdersQueryKey\|queryKey.*params.*\?\:" | head -5

Repository: orval-labs/orval

Length of output: 42


🏁 Script executed:

cat packages/query/src/flat-input.ts | head -100

Repository: orval-labs/orval

Length of output: 953


🏁 Script executed:

cat packages/query/src/query-generator.ts | head -150

Repository: orval-labs/orval

Length of output: 4080


🏁 Script executed:

# Look for the logic that handles the queryKey with params
rg "getGetUsersUserIdOrdersQueryKey|queryKey.*params" packages/query/src/ -A 3

Repository: orval-labs/orval

Length of output: 299


🏁 Script executed:

# Check if flat-input is a documented/intended pattern
find . -name "*.md" | xargs grep -l "flat.*input\|flat-input" 2>/dev/null | head -3

Repository: orval-labs/orval

Length of output: 42


🏁 Script executed:

# Look for the queryKey generation logic
rg "queryKey.*params\s*\?" packages/query/src/ -B 2 -A 2 | head -40

Repository: orval-labs/orval

Length of output: 42


🏁 Script executed:

# Check how params are passed in flat-input vs normal flow
sed -n '200,300p' packages/query/src/query-generator.ts | cat -n

Repository: orval-labs/orval

Length of output: 3402


🏁 Script executed:

# Look for where the queryKey helper is generated
rg "export const.*QueryKey.*=.*\(" packages/query/src/ -A 3 | head -30

Repository: orval-labs/orval

Length of output: 42


🏁 Script executed:

# Check the test setup for flat-input
find tests -name "*flat-input*" -type d | xargs ls -la

Repository: orval-labs/orval

Length of output: 932


🏁 Script executed:

# Look at a flat-input test case config
find tests -name "*flat-input*" -type f | grep -E "\.(yml|yaml|json)$" | head -3

Repository: orval-labs/orval

Length of output: 112


🏁 Script executed:

# Check if there's documentation in the issue or PR
git log --all --oneline --grep="flat" | head -10

Repository: orval-labs/orval

Length of output: 272


🏁 Script executed:

# Look for test files that might show expected behavior
find tests -name "*.test.ts" -o -name "*.spec.ts" | xargs grep -l "flat.*input\|flat-input" 2>/dev/null | head -3

Repository: orval-labs/orval

Length of output: 42


🏁 Script executed:

# Check what setQueryData helper looks like to understand cache invalidation
rg "setQueryData\|queryKey" tests/__snapshots__/react-query/use-set-query-data-multi-params/ -A 2 | head -40

Repository: orval-labs/orval

Length of output: 42


🏁 Script executed:

# Check the use-set-query-data snapshot to understand how setQueryData uses queryKey
sed -n '1,200p' tests/__snapshots__/react-query/use-set-query-data-multi-params/endpoints.ts | cat -n | grep -A 10 "setQueryData\|queryKey"

Repository: orval-labs/orval

Length of output: 1798


🏁 Script executed:

# Check if there are any tests that validate queryKey behavior
find tests -name "*.test.ts" | xargs grep -l "queryKey" | head -3

Repository: orval-labs/orval

Length of output: 42


🏁 Script executed:

# Look at how the generator decides to use the queryKey helper
rg "getGetUsersUserIdOrdersQueryKey\|queryKeyFnName" packages/query/src/ -B 2 -A 2 | head -50

Repository: orval-labs/orval

Length of output: 42


🏁 Script executed:

# Check if there are recent commits/PRs related to this issue
git log --all --oneline --grep="queryKey\|cache.*params\|flat.*input" --since="2024-01-01" | head -20

Repository: orval-labs/orval

Length of output: 410


🏁 Script executed:

# Look at the actual generated code to understand the current behavior more
cat tests/__snapshots__/react-query/flat-input-multi-params/endpoints.ts | sed -n '80,150p' | cat -n

Repository: orval-labs/orval

Length of output: 4285


🏁 Script executed:

# Check if this issue affects other snapshots
grep -r "params.*\?" tests/__snapshots__/react-query/flat-input*/ | grep "queryKey\|export const" | head -10

Repository: orval-labs/orval

Length of output: 1461


🏁 Script executed:

# Verify the problem by checking how invalidation would work
node - <<'NODE'
// Simulate the flat-input scenario
const getKey = (userId, params) => [`/users/${userId}/orders`, ...(params ? [params] : [])];

// When hook is called with just userId (flat input spreads empty params)
const hookKey = getKey(1, {});
console.log("Hook key:", JSON.stringify(hookKey));

// When invalidating with just the helper and userId (no params)
const invalidationKey = getKey(1);
console.log("Invalidation key:", JSON.stringify(invalidationKey));

// Are they equal?
console.log("Keys match:", JSON.stringify(hookKey) === JSON.stringify(invalidationKey));
NODE

Repository: orval-labs/orval

Length of output: 148


🏁 Script executed:

# Check the other location mentioned ("Also applies to: 93-97")
sed -n '93,97p' tests/__snapshots__/react-query/flat-input-multi-params/endpoints.ts | cat -n

Repository: orval-labs/orval

Length of output: 358


🏁 Script executed:

# Verify if the issue affects queryFn as well
node - <<'NODE'
// The queryFn also receives params, which affects the actual fetch call
const fetchUrl = (userId, params) => {
  // Simulate URL generation (params are used as query params)
  return `/users/${userId}/orders?` + new URLSearchParams(params).toString();
};

console.log("URL with empty object:", fetchUrl(1, {}));
console.log("URL with undefined:", fetchUrl(1, undefined));
console.log("URL with no params:", fetchUrl(1));
NODE

Repository: orval-labs/orval

Length of output: 173


🏁 Script executed:

# Check if there's a related fix or issue in recent commits
git log --all --oneline --all | grep -i "querykey\|cache.*key\|invalidat" | head -15

Repository: orval-labs/orval

Length of output: 1209


🏁 Script executed:

# Look at the specific commit that was mentioned in the logs
git show 32d946f8 --stat | head -30

Repository: orval-labs/orval

Length of output: 1397


🏁 Script executed:

# Verify the behavior matches what the review comment describes
node - <<'NODE'
// Test what actually happens with the current code
const getGetUsersUserIdOrdersQueryKey = (userId, params) => {
  return [`/users/${userId}/orders`, ...(params ? [params] : [])] ;
};

// Scenario 1: Hook called with flat input (only userId, no query params)
const flatInputKey = getGetUsersUserIdOrdersQueryKey(1, {});
console.log("Flat input hook creates key:", JSON.stringify(flatInputKey));

// Scenario 2: Manual invalidation using the helper (no params)
const invalidateKey = getGetUsersUserIdOrdersQueryKey(1);
console.log("Invalidation key:", JSON.stringify(invalidateKey));

// The problem
console.log("Cache invalidation will miss?", JSON.stringify(flatInputKey) !== JSON.stringify(invalidateKey));

// Scenario 3: With actual params
const withParamsKey = getGetUsersUserIdOrdersQueryKey(1, { status: 'pending' });
console.log("With actual params:", JSON.stringify(withParamsKey));
NODE

Repository: orval-labs/orval

Length of output: 245


🏁 Script executed:

# Check if there's a fix already in place for this specific issue
git show ddc25629 --stat | head -20

Repository: orval-labs/orval

Length of output: 891


🏁 Script executed:

# Look at the actual changes in that commit
git show ddc25629 | head -80

Repository: orval-labs/orval

Length of output: 3315


🏁 Script executed:

# Verify if the flat-input snapshots were updated in any recent commits
git log --oneline --all -- "tests/__snapshots__/react-query/flat-input*" | head -10

Repository: orval-labs/orval

Length of output: 99


🏁 Script executed:

# Check if there are any tests for flat-input behavior
find tests -name "*.test.ts" -o -name "*.spec.ts" | xargs grep -l "flat.*input\|flat-input" 2>/dev/null

Repository: orval-labs/orval

Length of output: 44


🏁 Script executed:

# Verify if the issue is acknowledged anywhere
rg "flat.*input.*params\|empty.*object.*cache\|queryKey.*fork" --type md | head -10

Repository: orval-labs/orval

Length of output: 42


🏁 Script executed:

# Double-check the actual hook usage to confirm the problem
sed -n '31,69p' tests/__snapshots__/react-query/flat-input-multi-params/endpoints.ts | cat -n

Repository: orval-labs/orval

Length of output: 1362


Empty params objects fork the React Query cache key, breaking cache invalidations.

With flat input pattern, when useGetUsersUserIdOrders({ userId }) is called without query params, the destructuring { userId, ...params } produces params = {}. This empty object is truthy, so getGetUsersUserIdOrdersQueryKey(userId, params) builds ['/users/1/orders', {}] instead of the ['/users/1/orders'] key that invalidations using getGetUsersUserIdOrdersQueryKey(userId) expect, causing cache misses.

Suggested fix
 const {query: queryOptions, fetch: fetchOptions} = options ?? {};
+  const hasParams = Object.values(params).some((value) => value !== undefined);

-  const queryKey =  queryOptions?.queryKey ?? getGetUsersUserIdOrdersQueryKey(userId,params);
+  const queryKey =  queryOptions?.queryKey ?? getGetUsersUserIdOrdersQueryKey(
+    userId,
+    hasParams ? params : undefined,
+  );

-    const queryFn: QueryFunction<Awaited<ReturnType<typeof getUsersUserIdOrders>>> = ({ signal }) => getUsersUserIdOrders(userId,params, { signal, ...fetchOptions });
+    const queryFn: QueryFunction<Awaited<ReturnType<typeof getUsersUserIdOrders>>> = ({ signal }) =>
+      getUsersUserIdOrders(
+        userId,
+        hasParams ? params : undefined,
+        { signal, ...fetchOptions },
+      );

Also applies to: 93-97

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/__snapshots__/react-query/flat-input-multi-params/endpoints.ts` around
lines 80 - 85, The query key builder getGetUsersUserIdOrdersQueryKey currently
treats an empty params object as present and appends it to the key, causing
cache forks; update the function to only append params when they are
non-null/undefined AND not an empty object (e.g., check params &&
Object.keys(params).length > 0) so getGetUsersUserIdOrdersQueryKey(userId) and
getGetUsersUserIdOrdersQueryKey(userId, {}) produce the same key; apply the same
empty-object guard to the other similar query key builders in this diff.

Comment on lines +88 to +89
export const getGetUsersUserIdOrdersQueryOptions = <TData = Awaited<ReturnType<typeof getUsersUserIdOrders>>, TError = unknown>({ userId, ...params }: { userId: number } & GetUsersUserIdOrdersParams, options?: { query?:Partial<UseQueryOptions<Awaited<ReturnType<typeof getUsersUserIdOrders>>, TError, TData>>, fetch?: RequestInit}
) => {
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.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Compare the flat-input helper signature with the hook call site.
sed -n '88,145p' tests/__snapshots__/react-query/flat-input-multi-params/endpoints.ts

Repository: orval-labs/orval

Length of output: 3536


useGetUsersUserIdOrders still calls the helper with incorrect argument shape.

The hook signature expects and receives { userId, ...params } as a flat object, but it passes (userId, params, options) to getGetUsersUserIdOrdersQueryOptions. The helper now expects { userId, ...params } as its first argument; the current call passes positional arguments instead, leaving userId undestructured and producing an incorrect query key.

Fix
-  const queryOptions = getGetUsersUserIdOrdersQueryOptions(userId,params,options)
+  const queryOptions = getGetUsersUserIdOrdersQueryOptions({ userId, ...params }, options)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/__snapshots__/react-query/flat-input-multi-params/endpoints.ts` around
lines 88 - 89, useGetUsersUserIdOrders is calling
getGetUsersUserIdOrdersQueryOptions with positional args (userId, params,
options) but the helper's signature expects a single flat object ({ userId,
...params }) and optional options; update the call site in
useGetUsersUserIdOrders to pass one object merging userId and params (e.g., {
userId, ...params }) as the first argument and pass options as the second
argument so the query key is built from the correct shape for
getGetUsersUserIdOrdersQueryOptions.

Comment on lines +77 to +79
export const getSearchItemsMutationOptions = <TError = unknown,
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof searchItems>>, TError,{{ orgId: string } & SearchItemsParams & SearchItemsBody}, TContext>, fetch?: RequestInit}
): UseMutationOptions<Awaited<ReturnType<typeof searchItems>>, TError,{{ orgId: string } & SearchItemsParams & SearchItemsBody}, TContext> => {
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.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Expect the emitted type to be a plain intersection and the call to follow
# `(orgId, data, params, options)`. The current snapshot shows both regressions.
sed -n '54,119p' tests/__snapshots__/react-query/flat-input-path-query-body/endpoints.ts

Repository: orval-labs/orval

Length of output: 2628


Flat-input searchItems mutation is emitted with invalid types and the wrong call order.

The type parameters use {{ orgId: string } & ...} with double braces (invalid syntax), and mutationFn calls searchItems(orgId, { page, limit }, data, fetchOptions) when it should be searchItems(orgId, data, { page, limit }, fetchOptions) to match the function signature (orgId, searchItemsBody, params?, options?). The double-brace syntax error appears in both getSearchItemsMutationOptions (lines 77–79) and useSearchItems (lines 111–116), while the argument order error is in the mutationFn (lines 91–95).

Suggested fix
 export const getSearchItemsMutationOptions = <TError = unknown,
-    TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof searchItems>>, TError,{{ orgId: string } & SearchItemsParams & SearchItemsBody}, TContext>, fetch?: RequestInit}
-): UseMutationOptions<Awaited<ReturnType<typeof searchItems>>, TError,{{ orgId: string } & SearchItemsParams & SearchItemsBody}, TContext> => {
+    TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof searchItems>>, TError,{ orgId: string } & SearchItemsParams & SearchItemsBody, TContext>, fetch?: RequestInit}
+): UseMutationOptions<Awaited<ReturnType<typeof searchItems>>, TError,{ orgId: string } & SearchItemsParams & SearchItemsBody, TContext> => {
@@
-          return  searchItems(orgId, { page, limit }, data,fetchOptions)
+          return  searchItems(orgId, data, { page, limit }, fetchOptions)

Apply the same brace fix in useSearchItems below.

🧰 Tools
🪛 Biome (2.4.9)

[error] 77-77: expected > but instead found =

(parse)


[error] 77-77: Invalid assignment to <TError

(parse)


[error] 78-78: Expected an expression, or an assignment but instead found ':'.

(parse)


[error] 78-78: expected , but instead found ?

(parse)


[error] 78-78: Expected a property, a shorthand property, a getter, a setter, or a method but instead found '{{ orgId'.

(parse)


[error] 78-78: expected , but instead found :

(parse)


[error] 78-78: expected ) but instead found }

(parse)


[error] 79-79: Expected a statement but instead found '& SearchItemsParams & SearchItemsBody'.

(parse)


[error] 79-79: Expected a statement but instead found ', TContext> =>'.

(parse)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/__snapshots__/react-query/flat-input-path-query-body/endpoints.ts`
around lines 77 - 79, The emitted mutation types and call order are wrong:
remove the stray double braces so the generic target type becomes "{ orgId:
string } & SearchItemsParams & SearchItemsBody" (fix both
getSearchItemsMutationOptions and useSearchItems) and correct the mutationFn
invocation to call searchItems(orgId, data, { page, limit }, fetchOptions) to
match the function signature (searchItems(orgId, searchItemsBody, params?,
options?)). Ensure mutationFn references the same parameter names used in the
file (e.g., orgId, data, page, limit, fetchOptions) and update both occurrences
noted (getSearchItemsMutationOptions, useSearchItems, and the mutationFn block).

Comment on lines +244 to +246
export const getCreatePetsMutationOptions = <TError = Error,
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof createPets>>, TError,{CreatePetsParams & CreatePetsBody}, TContext>, fetch?: RequestInit}
): UseMutationOptions<Awaited<ReturnType<typeof createPets>>, TError,{CreatePetsParams & CreatePetsBody}, TContext> => {
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.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Expect the helper to accept `headers` and call
# `createPets(data, params, headers, options)`. The current snapshot does neither.
sed -n '221,286p' tests/__snapshots__/react-query/flat-input-petstore/endpoints.ts

Repository: orval-labs/orval

Length of output: 2464


Flat-input createPets generation has invalid TypeScript syntax, drops required headers parameter, and swaps body/params argument order.

The variables type is emitted as {CreatePetsParams & CreatePetsBody} (invalid TS—braces should not wrap an intersection type), and neither getCreatePetsMutationOptions nor useCreatePets accepts the required CreatePetsHeaders parameter. The createPets function call also passes arguments in wrong order: currently createPets({ limit, sort }, data, fetchOptions) but should be createPets(data, { limit, sort }, headers, fetchOptions).

Suggested fix
 export const getCreatePetsMutationOptions = <TError = Error,
-    TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof createPets>>, TError,{CreatePetsParams & CreatePetsBody}, TContext>, fetch?: RequestInit}
-): UseMutationOptions<Awaited<ReturnType<typeof createPets>>, TError,{CreatePetsParams & CreatePetsBody}, TContext> => {
+    TContext = unknown>(headers: CreatePetsHeaders, options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof createPets>>, TError, CreatePetsParams & CreatePetsBody, TContext>, fetch?: RequestInit}
+): UseMutationOptions<Awaited<ReturnType<typeof createPets>>, TError, CreatePetsParams & CreatePetsBody, TContext> => {
@@
-          return  createPets({ limit, sort }, data,fetchOptions)
+          return  createPets(data, { limit, sort }, headers, fetchOptions)

The same headers parameter needs to flow through useCreatePets.

Also applies to: 258-262, 278-286

🧰 Tools
🪛 Biome (2.4.9)

[error] 244-244: expected > but instead found =

(parse)


[error] 244-244: Invalid assignment to <TError

(parse)


[error] 245-245: Expected an expression, or an assignment but instead found ':'.

(parse)


[error] 245-245: expected , but instead found ?

(parse)


[error] 245-245: Expected a property, a shorthand property, a getter, a setter, or a method but instead found '{CreatePetsParams & CreatePetsBody'.

(parse)


[error] 245-245: Expected an expression but instead found ','.

(parse)


[error] 245-245: Expected an expression, or an assignment but instead found ':'.

(parse)


[error] 245-245: expected ) but instead found }

(parse)


[error] 246-246: Expected a statement but instead found ', TContext> =>'.

(parse)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/__snapshots__/react-query/flat-input-petstore/endpoints.ts` around
lines 244 - 246, The generated TypeScript has three issues: the variable type is
emitted as "{CreatePetsParams & CreatePetsBody}" (invalid — remove the braces),
the required CreatePetsHeaders parameter is missing from
getCreatePetsMutationOptions and useCreatePets (add a headers?:
CreatePetsHeaders parameter and thread it through), and the createPets call uses
the wrong argument order; change calls from createPets({ limit, sort }, data,
fetchOptions) to createPets(data, { limit, sort }, headers, fetchOptions).
Update the UseMutationOptions generic type to use CreatePetsParams &
CreatePetsBody (without braces) and apply the same fixes to the other
occurrences noted (around the other get*/use* functions at the referenced
blocks).

Comment thread tests/configs/react-query.config.ts
@inas-sirhan
Copy link
Copy Markdown
Contributor Author

Working on the problems

- Fix double-wrapped braces in mutation variable types by threading
  overrideVariableType through getQueryOptionsDefinition and generateQueryArguments
- Keep definitions in original format for downstream compatibility
- Use separate flatVariableType for intersection types in mutationFn and options
- Fix prettier: true -> formatter: 'prettier' in test configs
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
tests/__snapshots__/react-query/flat-input-path-query-body/endpoints.ts (1)

91-95: ⚠️ Potential issue | 🔴 Critical

Wrong argument order in mutationFn - body and query params are swapped.

The searchItems function signature is (orgId, searchItemsBody, params?, options?), but the mutationFn calls it with (orgId, { page, limit }, data, fetchOptions). This passes query params where body is expected and vice versa, causing incorrect API behavior at runtime.

The generator's flatMutationCallArgs builds arguments as [pathArgs, queryArgs, bodyArg] but the underlying fetch function expects [path, body, query].

🐛 Expected fix in generated output
       const mutationFn: MutationFunction<Awaited<ReturnType<typeof searchItems>>, { orgId: string } & SearchItemsParams & SearchItemsBody> = (props) => {
           const {orgId, page, limit, ...data} = props ?? {};

-          return  searchItems(orgId, { page, limit }, data,fetchOptions)
+          return  searchItems(orgId, data, { page, limit },fetchOptions)
         }

The root cause is in the generator (mutation-generator.ts around line 176) where flatMutationCallArgs is assembled - the bodyArg and queryArgs order should be swapped to match the function signature.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/__snapshots__/react-query/flat-input-path-query-body/endpoints.ts`
around lines 91 - 95, The mutation is calling searchItems with the wrong
argument order: searchItems(orgId, searchItemsBody, params?, options?) but
mutationFn passes (orgId, { page, limit }, data, fetchOptions); fix the
generator in mutation-generator.ts where flatMutationCallArgs is assembled by
swapping the body and query positions so the built args become [pathArgs,
bodyArg, queryArgs] (not [pathArgs, queryArgs, bodyArg]); update the code that
produces mutationFn so mutationFn invokes searchItems(orgId, data, { page, limit
}, fetchOptions) to match the searchItems signature.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/query/src/query-options.ts`:
- Around line 79-80: The overrideVariableType value must be treated as falsy
when it's an empty string to avoid producing invalid generics like "TError,,
TContext"; update usages of overrideVariableType in query-options.ts (and the
code paths that receive flatVariableType from mutation-generator.ts) to sanitize
it before interpolation by replacing the nullish coalescing (??) check with a
truthy check (e.g., use overrideVariableType &&
overrideVariableType.trim().length ? overrideVariableType : undefined or
similar) so empty strings become undefined; apply the same guard to the other
occurrences referenced (around the overrideVariableType/type bindings and the
generic interpolation sites) so the mutation generics render correctly without
double commas.

In `@tests/__snapshots__/react-query/flat-input-path-query-body/endpoints.ts`:
- Around line 31-34: The generated type export type searchItemsResponseSuccess
includes an extra stray semicolon after its closing brace; update the template
or generator code that emits the type (the code that prints/export types like
searchItemsResponseSuccess) to avoid emitting an additional ";" after the type
declaration—locate the emitter for type aliases or the template that
concatenates "};" and ensure it does not append a second standalone semicolon,
then regenerate the output to confirm only a single terminating semicolon is
produced.

---

Duplicate comments:
In `@tests/__snapshots__/react-query/flat-input-path-query-body/endpoints.ts`:
- Around line 91-95: The mutation is calling searchItems with the wrong argument
order: searchItems(orgId, searchItemsBody, params?, options?) but mutationFn
passes (orgId, { page, limit }, data, fetchOptions); fix the generator in
mutation-generator.ts where flatMutationCallArgs is assembled by swapping the
body and query positions so the built args become [pathArgs, bodyArg, queryArgs]
(not [pathArgs, queryArgs, bodyArg]); update the code that produces mutationFn
so mutationFn invokes searchItems(orgId, data, { page, limit }, fetchOptions) to
match the searchItems signature.
🪄 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: cfabe007-21c3-40b6-86c7-ad63568a7b51

📥 Commits

Reviewing files that changed from the base of the PR and between dce0b67 and 2fcb230.

📒 Files selected for processing (9)
  • packages/query/src/framework-adapter.ts
  • packages/query/src/frameworks/angular.ts
  • packages/query/src/frameworks/index.ts
  • packages/query/src/frameworks/svelte.ts
  • packages/query/src/mutation-generator.ts
  • packages/query/src/query-options.ts
  • tests/__snapshots__/react-query/flat-input-path-query-body/endpoints.ts
  • tests/__snapshots__/react-query/flat-input-petstore/endpoints.ts
  • tests/configs/react-query.config.ts
✅ Files skipped from review due to trivial changes (1)
  • tests/snapshots/react-query/flat-input-petstore/endpoints.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/configs/react-query.config.ts
  • packages/query/src/mutation-generator.ts

Comment thread packages/query/src/query-options.ts
Comment thread tests/__snapshots__/react-query/flat-input-path-query-body/endpoints.ts Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (2)
tests/__snapshots__/react-query/flat-input-multi-params/endpoints.ts (2)

224-228: ⚠️ Potential issue | 🔴 Critical

Fix useGetUsersUserIdOrders to pass the flat-input object shape.

Lines 224-228 call getGetUsersUserIdOrdersQueryOptions with positional args, but its signature expects a single flat object plus optional options.

Suggested fix
-  const queryOptions = getGetUsersUserIdOrdersQueryOptions(
-    userId,
-    params,
-    options,
-  );
+  const queryOptions = getGetUsersUserIdOrdersQueryOptions(
+    { userId, ...params },
+    options,
+  );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/__snapshots__/react-query/flat-input-multi-params/endpoints.ts` around
lines 224 - 228, The call to getGetUsersUserIdOrdersQueryOptions currently
passes positional args (userId, params, options) but the function expects a
single flat input object plus optional options; update the call in
useGetUsersUserIdOrders to pass a single object merging the route param and
query/body params (e.g. { userId, ...params }) as the first argument and then
pass options as the second argument, so getGetUsersUserIdOrdersQueryOptions
receives the flat input shape it expects.

103-109: ⚠️ Potential issue | 🟠 Major

Avoid cache-key fork when flat input has no query fields.

Line 104 always passes params (rest object), so {} becomes part of the query key and diverges from the key built without params.

Suggested fix
   const { query: queryOptions, fetch: fetchOptions } = options ?? {};
+  const hasParams = Object.values(params).some((value) => value !== undefined);

   const queryKey =
-    queryOptions?.queryKey ?? getGetUsersUserIdOrdersQueryKey(userId, params);
+    queryOptions?.queryKey ??
+    getGetUsersUserIdOrdersQueryKey(userId, hasParams ? params : undefined);

   const queryFn: QueryFunction<
     Awaited<ReturnType<typeof getUsersUserIdOrders>>
   > = ({ signal }) =>
-    getUsersUserIdOrders(userId, params, { signal, ...fetchOptions });
+    getUsersUserIdOrders(userId, hasParams ? params : undefined, {
+      signal,
+      ...fetchOptions,
+    });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/__snapshots__/react-query/flat-input-multi-params/endpoints.ts` around
lines 103 - 109, The query key diverges because you always pass the rest object
params (even empty {}) into getGetUsersUserIdOrdersQueryKey and
getUsersUserIdOrders; change both call sites to only pass params when it has
properties (e.g., params && Object.keys(params).length ? params : undefined) so
an empty object is not included in the cache key or request; update the queryKey
expression (the call to getGetUsersUserIdOrdersQueryKey) and the queryFn
invocation of getUsersUserIdOrders accordingly to use this conditional params
value.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@tests/__snapshots__/react-query/flat-input-multi-params/endpoints.ts`:
- Around line 224-228: The call to getGetUsersUserIdOrdersQueryOptions currently
passes positional args (userId, params, options) but the function expects a
single flat input object plus optional options; update the call in
useGetUsersUserIdOrders to pass a single object merging the route param and
query/body params (e.g. { userId, ...params }) as the first argument and then
pass options as the second argument, so getGetUsersUserIdOrdersQueryOptions
receives the flat input shape it expects.
- Around line 103-109: The query key diverges because you always pass the rest
object params (even empty {}) into getGetUsersUserIdOrdersQueryKey and
getUsersUserIdOrders; change both call sites to only pass params when it has
properties (e.g., params && Object.keys(params).length ? params : undefined) so
an empty object is not included in the cache key or request; update the queryKey
expression (the call to getGetUsersUserIdOrdersQueryKey) and the queryFn
invocation of getUsersUserIdOrders accordingly to use this conditional params
value.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b93ea9d9-c4c5-4727-96d7-05ecf661cab7

📥 Commits

Reviewing files that changed from the base of the PR and between 2fcb230 and 3d5fcbe.

📒 Files selected for processing (22)
  • tests/__snapshots__/react-query/flat-input-multi-params/endpoints.ts
  • tests/__snapshots__/react-query/flat-input-multi-params/model/getUsersUserIdOrdersParams.ts
  • tests/__snapshots__/react-query/flat-input-multi-params/model/getUsersUserIdOrdersStatus.ts
  • tests/__snapshots__/react-query/flat-input-path-query-body/endpoints.ts
  • tests/__snapshots__/react-query/flat-input-path-query-body/model/searchItemsParams.ts
  • tests/__snapshots__/react-query/flat-input-petstore/endpoints.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/catType.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/createPetsHeaders.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/createPetsParams.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/createPetsSort.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/createPetsXExample.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/dachshundBreed.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/dog.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/dogType.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/labradoodleBreed.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/listPetsHeaders.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/listPetsParams.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/listPetsSort.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/listPetsXExample.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/pet.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/petCallingCode.ts
  • tests/__snapshots__/react-query/flat-input-petstore/model/petCountry.ts
✅ Files skipped from review due to trivial changes (15)
  • tests/snapshots/react-query/flat-input-petstore/model/listPetsHeaders.ts
  • tests/snapshots/react-query/flat-input-path-query-body/model/searchItemsParams.ts
  • tests/snapshots/react-query/flat-input-petstore/model/dogType.ts
  • tests/snapshots/react-query/flat-input-petstore/model/createPetsParams.ts
  • tests/snapshots/react-query/flat-input-petstore/model/petCallingCode.ts
  • tests/snapshots/react-query/flat-input-petstore/model/createPetsXExample.ts
  • tests/snapshots/react-query/flat-input-multi-params/model/getUsersUserIdOrdersParams.ts
  • tests/snapshots/react-query/flat-input-multi-params/model/getUsersUserIdOrdersStatus.ts
  • tests/snapshots/react-query/flat-input-petstore/model/labradoodleBreed.ts
  • tests/snapshots/react-query/flat-input-petstore/model/dog.ts
  • tests/snapshots/react-query/flat-input-petstore/model/listPetsXExample.ts
  • tests/snapshots/react-query/flat-input-petstore/model/listPetsSort.ts
  • tests/snapshots/react-query/flat-input-petstore/model/createPetsHeaders.ts
  • tests/snapshots/react-query/flat-input-petstore/model/listPetsParams.ts
  • tests/snapshots/react-query/flat-input-petstore/model/pet.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • tests/snapshots/react-query/flat-input-petstore/model/petCountry.ts
  • tests/snapshots/react-query/flat-input-petstore/model/createPetsSort.ts
  • tests/snapshots/react-query/flat-input-petstore/model/catType.ts
  • tests/snapshots/react-query/flat-input-petstore/model/dachshundBreed.ts
  • tests/snapshots/react-query/flat-input-path-query-body/endpoints.ts
  • tests/snapshots/react-query/flat-input-petstore/endpoints.ts

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
packages/query/src/query-options.ts (1)

106-107: ⚠️ Potential issue | 🟠 Major

Use sanitize + ?? to satisfy lint without reintroducing empty-string generic breakage.

Line 107 currently fixes empty-string fallback behavior, but CI fails on prefer-nullish-coalescing. Switching directly to ?? can bring back invalid mutation generics when overrideVariableType === '' (downstream at Line 184/Line 189). Use a sanitized intermediate value.

Suggested fix
-  const varType =
-    overrideVariableType || (definitions ? `{${definitions}}` : 'void');
+  const sanitizedOverrideVariableType =
+    typeof overrideVariableType === 'string' &&
+    overrideVariableType.trim().length > 0
+      ? overrideVariableType
+      : undefined;
+  const varType =
+    sanitizedOverrideVariableType ?? (definitions ? `{${definitions}}` : 'void');
#!/bin/bash
set -euo pipefail

# Verify root-cause line now uses sanitize + nullish coalescing.
fd -i "query-options.ts" --exec rg -n -C2 "sanitizedOverrideVariableType|const varType|\\?\\?"

# Ensure raw `overrideVariableType ||` fallback is no longer present.
fd -i "query-options.ts" --exec rg -n "overrideVariableType\\s*\\|\\|" || true
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/query/src/query-options.ts` around lines 106 - 107, The current
fallback uses || which CI flags; create a sanitized intermediate like
sanitizedOverrideVariableType that maps an empty string to undefined (e.g.,
const sanitizedOverrideVariableType = overrideVariableType === '' ? undefined :
overrideVariableType) and then replace the varType assignment with nullish
coalescing: const varType = sanitizedOverrideVariableType ?? (definitions ?
`{${definitions}}` : 'void'); update references to use varType and remove the
raw "overrideVariableType ||" pattern; this preserves empty-string protection
while satisfying prefer-nullish-coalescing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@packages/query/src/query-options.ts`:
- Around line 106-107: The current fallback uses || which CI flags; create a
sanitized intermediate like sanitizedOverrideVariableType that maps an empty
string to undefined (e.g., const sanitizedOverrideVariableType =
overrideVariableType === '' ? undefined : overrideVariableType) and then replace
the varType assignment with nullish coalescing: const varType =
sanitizedOverrideVariableType ?? (definitions ? `{${definitions}}` : 'void');
update references to use varType and remove the raw "overrideVariableType ||"
pattern; this preserves empty-string protection while satisfying
prefer-nullish-coalescing.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7ad1f582-9bf6-4f0a-86d7-fc629e8c9f4d

📥 Commits

Reviewing files that changed from the base of the PR and between 3d5fcbe and 47475c0.

📒 Files selected for processing (1)
  • packages/query/src/query-options.ts

@melloware melloware marked this pull request as draft March 30, 2026 11:25
- Consolidate all flat input logic into buildFlatInput() in flat-input.ts
- Both query and mutation generators use the same utility
- Fix mutation argument order to match client function signatures
- Fix double-wrapped braces in mutation variable types via overrideVariableType
- Skip flat input for mutations with headers (non-standard field names)
- Add comprehensive test spec covering all 12 scenarios:
  no params, 1/2 path params, 1/2 query params, path+query,
  2path+query, body only, path+body, query+body, path+query+body,
  delete with 2 path params
@melloware melloware requested review from anymaniax and snebjorn March 31, 2026 13:21
@melloware
Copy link
Copy Markdown
Collaborator

@anymaniax @snebjorn i have no idea if this use case makes sense to support. Please review.

@inas-sirhan
Copy link
Copy Markdown
Contributor Author

@melloware
Can you think of downsides of such a feature?
Is there cases where it might be problematic?

I personally used it and prefer it (used post processing for it) in my react, zod, react-hook-form apps.

I also remember seeing it on Discord from long time ago, and a maintainer said a PR is welcome or so

If another solution (maybe less invasive?) is preferred I can try other approaches and see where we land

Copy link
Copy Markdown
Contributor

@snebjorn snebjorn left a comment

Choose a reason for hiding this comment

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

I didn't review the code, but rather the concept.

OAS 3.1 allows duplicate parameter names. This is valid

paths:
  /users/{id}:
    get:
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
        - in: query
          name: id
          schema:
            type: string

and would result in getUsers({id: ..., id: ...}) which doesn't work.

@inas-sirhan
Copy link
Copy Markdown
Contributor Author

@snebjorn
the code will throw error in such cases, so it won't allow bugs
and i believe it's rare someone would use same field name in path and query/body (confusing).

@snebjorn
Copy link
Copy Markdown
Contributor

snebjorn commented Apr 1, 2026

IMO throwing an error on valid OAS is a bug, no matter how poorly the OAS was written 😄

Furthermore I'd be cautious about adding more state/options to an already messy/complex codebase. I wouldn't make this an option but rather switch to it instead. But the duplicate key issue would have to be addressed. And I'm not convinced it would be a net benefit, given the friction it would cause the users.

@inas-sirhan
Copy link
Copy Markdown
Contributor Author

inas-sirhan commented Apr 2, 2026

@snebjorn
How about for such cases we simply fallback to normal behavior? (i.e separated instead of flattened)
and document that behavior?

This would solve the issue cleanly without throwing an error!

It's your call but I believe it's a good and beneficial thing to have (from my personal experience), because it's just boilerplate to split the fields, especially when all fields are unique.
Those who want it simply opt in for it, and those who don't, get the normal Orval's behavior

Lemme know if you want this, I can try to minimize changes and prop drilling to make it more maintainable in the future

@melloware melloware added the enhancement New feature or request label May 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(react-query): merge path params, query params, and body into a single object (flattened)

4 participants