Skip to content

fix: Avoid schema key collision when extracting request params - #1835

Merged
js2me merged 1 commit into
acacode:mainfrom
ryanb93:fix/extract-request-params-schema-key-collision
Sep 18, 2026
Merged

js2me merged 1 commit into
acacode:mainfrom
ryanb93:fix/extract-request-params-schema-key-collision

Conversation

@ryanb93

@ryanb93 ryanb93 commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Problem

With extractRequestParams: true, the extracted <operationId>Params type is registered as #/components/schemas/<operationId>Params without checking whether that key already exists in the spec. When it does, the existing model is silently overwritten.

This is easy to hit in practice: an operation getOrder whose request body model is called GetOrderParams. Since 13.2.9 (#1381) path-only routes also produce an extracted params type, so any route with a path parameter is exposed, not just routes with query params.

Given this spec fragment:

"/stores/{storeId}/orders/preview": {
  "post": {
    "operationId": "getOrder",
    "parameters": [{ "name": "storeId", "in": "path", "required": true, "schema": { "type": "string" } }],
    "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GetOrderParams" } } } }
  }
}

the generated output on main is:

export interface GetOrderParams {
  storeId: string;          // the body model's fields are gone
}

getOrder: ({ storeId }: GetOrderParams, data: GetOrderParams, params: RequestParams = {}) =>

The method still compiles, so nothing flags it — data is just typed as the path params instead of the body.

Solution

extractResponseBody and extractResponseError already handle this via extractTypeNameWithoutSchemaKeyCollision, which checks the candidate name against the registered components and reserves/retries until it finds a free one. createRequestParamsSchema called resolveTypeName directly and skipped that guard. This PR routes it through the same helper.

With the fix the output becomes:

export interface GetOrderParams {
  items: string[];
  currency: string;
  couponCode?: string;
}

export interface GetOrderParams1 {
  storeId: string;
}

getOrder: ({ storeId }: GetOrderParams1, data: GetOrderParams, params: RequestParams = {}) =>

The 1 suffix is the existing fallback behaviour of NameResolver, consistent with how response-body collisions are already resolved. Users who want a nicer name can still supply extractingOptions.requestParamsNameResolver.

I considered instead reserving all schema names in componentTypeNameResolver up front, but that would change name resolution for every extractor and is a larger behavioural change than this bug warrants.

Verification

  • New spec test tests/spec/extractRequestParams-schema-name-collision with a minimal schema reproducing the collision (path-param route + same-named body model, plus a control route with path + query params). It fails on main (the body model's fields disappear) and passes with this change.
  • Full suite: 96 files / 289 tests pass with no other snapshot changes, so existing specs without a collision are unaffected.
  • bun run format:check is clean. bun run lint reports the same pre-existing noExplicitAny warnings as main; nothing new in the touched files.

Summary by cubic

Fixes extractRequestParams overwriting a component schema when its name matches <operationId>Params (for example a getOrder operation whose request body model is GetOrderParams). Route params now use the same schema-key collision guard as response extractors, so the original model keeps its name and the extracted params type is renamed to the next free name like GetOrderParams1.

Notes

  • Since 13.2.9, route params are extracted for path-only routes, so this can affect any route with a path parameter.
  • The suffix fallback matches how response-body collisions are already resolved; a custom name can be set via extractingOptions.requestParamsNameResolver.
  • Existing specs without a collision are unaffected, and a new spec test covers the collision.

Written for commit 38190c6. Summary will update on new commits.

Review in cubic

With extractRequestParams enabled, the generated <operationId>Params type
was registered without checking whether components.schemas already had a
schema of that name. When it did (e.g. a getOrder operation whose request
body model is called GetOrderParams), the model was overwritten by the
route's path/query params and the method's body argument was typed as the
path params.

Route createRequestParamsSchema through the same schema-key collision
guard that extractResponseBody and extractResponseError already use.

Co-authored-by: Cursor <cursoragent@cursor.com>
@changeset-bot

changeset-bot Bot commented Sep 15, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 38190c6

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
swagger-typescript-api Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No issues found across 5 files

Re-trigger cubic

@ryanb93 ryanb93 changed the title Avoid schema key collision when extracting request params fix: Avoid schema key collision when extracting request params Sep 15, 2026
@js2me

js2me commented Sep 18, 2026

Copy link
Copy Markdown
Member

@ryanb93 thank you!

@js2me
js2me merged commit 18023ad into acacode:main Sep 18, 2026
5 checks passed
@github-actions github-actions Bot mentioned this pull request Sep 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants