Skip to content

fix(plugin): avoid duplicate keys when auto-generating @ApiOperation#3871

Merged
kamilmysliwiec merged 1 commit into
nestjs:masterfrom
yogeshwaran-c:fix/plugin-api-operation-dedupe-summary
Apr 22, 2026
Merged

fix(plugin): avoid duplicate keys when auto-generating @ApiOperation#3871
kamilmysliwiec merged 1 commit into
nestjs:masterfrom
yogeshwaran-c:fix/plugin-api-operation-dedupe-summary

Conversation

@yogeshwaran-c
Copy link
Copy Markdown
Contributor

What kind of change does this PR introduce?

Bug fix (plugin / transformer).

What is the current behavior?

When introspectComments is enabled and the user has already supplied summary (or description, depending on controllerKeyOfComment) on an explicit @ApiOperation({...}), the plugin still unshifts an auto-generated key, producing a duplicate-key object literal.

Minimal repro controller:

/** doc-summary */
@ApiOperation({ summary: 'user-summary' })
@Get()
findOne() {}

Transpiles to:

openapi.ApiOperation({ summary: \"doc-summary\", summary: 'user-summary' })

Runtime behaviour happens to be correct (JS picks the last duplicate key) but:

  1. the emitted JS is a duplicate-key object literal, which linters flag in strict mode;
  2. it is inconsistent with how the plugin already guards description (via hasRemarksKey) and deprecated (via hasDeprecatedKey) against the exact same scenario.

What is the new behavior?

The unshift only runs when the explicit @ApiOperation({...}) argument does not already contain the generated key. So the same input now produces:

openapi.ApiOperation({ summary: 'user-summary' })

All other cases are unchanged: empty @ApiOperation({}), missing decorator, @remarks blocks, and mixed summary + description combinations still work exactly as before.

Additional context

Added a new fixture test/plugin/fixtures/app.controller-api-operation-dedupe.ts and a corresponding test in controller-class-visitor.spec.ts covering both the plain and the @remarks-combined cases. The existing appControllerTextTranspiled snapshot is untouched because none of its methods override the generated key.

@kamilmysliwiec
Copy link
Copy Markdown
Member

could you resolve conflicts?

When `introspectComments` is enabled, the plugin unshifts an auto-
generated `summary` (or `description`, depending on
`controllerKeyOfComment`) onto the @apioperation decorator's argument
object. It did not check whether the user had already supplied the same
key, so a method annotated as:

  /** doc-summary */
  @apioperation({ summary: 'user-summary' })

was transpiled to:

  openapi.ApiOperation({ summary: 'doc-summary', summary: 'user-summary' })

producing a duplicate-key object literal. Runtime behaviour happened to
be correct (JavaScript picks the last one) but the output is clearly
incorrect, trips strict linters, and is inconsistent with how the
plugin already handles `description` (via `hasRemarksKey`) and
`deprecated` (via `hasDeprecatedKey`).

Skip the unshift when the existing @apioperation argument already
contains the same key. No behaviour change for the common cases where
the user does not explicitly set `summary`/`description`.
@yogeshwaran-c yogeshwaran-c force-pushed the fix/plugin-api-operation-dedupe-summary branch from 09e0f07 to 0afb905 Compare April 22, 2026 07:33
@yogeshwaran-c
Copy link
Copy Markdown
Contributor Author

Conflicts resolved (rebased onto master). CI re-running.

@kamilmysliwiec kamilmysliwiec merged commit 14bd8f5 into nestjs:master Apr 22, 2026
1 check passed
@kamilmysliwiec
Copy link
Copy Markdown
Member

lgtm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants