Skip to content

fix(plugin): infer response type for models imported from workspace p… - #4008

Open
thegoldenflow wants to merge 1 commit into
nestjs:masterfrom
thegoldenflow:fix/controller-workspace-response-type
Open

fix(plugin): infer response type for models imported from workspace p…#4008
thegoldenflow wants to merge 1 commit into
nestjs:masterfrom
thegoldenflow:fix/controller-workspace-response-type

Conversation

@thegoldenflow

@thegoldenflow thegoldenflow commented Jul 15, 2026

Copy link
Copy Markdown

The controller visitor bailed whenever the inferred response type resolved through node_modules (e.g. a pnpm-symlinked workspace package), emitting ApiResponse without a type and leaving the operation with no response schema. Remove that guard so control falls through to the existing typeReferenceToIdentifier -> replaceImportPath -> normalizePackagePath chain, aligning the controller response path with the model/property path (#3798).

Fixes #4006

PR Checklist

PR Type

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Other

What is the current behavior?

Issue Number: #4006

When a controller method's return type is a DTO imported from a workspace package that resolves through node_modules (pnpm symlink), the CLI plugin resolves the type name as import("/abs/path/node_modules/@scope/pkg/dist/index").Dto, hits the typeName.includes('node_modules') guard in ControllerClassVisitor#createTypePropertyAssignment(), and returns early. The generated code is openapi.ApiResponse({ status: 200 }) with no type, so the OpenAPI operation has no response content schema. The DTO's own _OPENAPI_METADATA_FACTORY is unaffected — only the inferred controller response reference is lost.

What is the new behavior?

The guard is removed, so the resolved type flows into typeReferenceToIdentifier -> replaceImportPath -> normalizePackagePath, which strips the node_modules prefix and emits the package specifier. The generated response now carries the type, e.g. openapi.ApiResponse({ status: 200, type: require('@scope/pkg').Dto }), and the operation gets its response schema. This matches how the model/property path already handles workspace-package imports (#3798).

Does this PR introduce a breaking change?

  • Yes
  • No

Additive to the generated OpenAPI document (response schemas that were previously absent now appear). See Other information for the behavioral scope.

Other information

Scope / tradeoff. Removing the guard means the controller response path now emits a type for any return type that resolves through node_modules, not only symlinked workspace packages — e.g. StreamableFile, Buffer, or rxjs types would surface a response schema where previously none was emitted. This is the same tradeoff already accepted for the model/property path in #3798; the blast radius is somewhat larger here since controller return types more often reference framework/third-party types. If you'd prefer to avoid broadening the default, I'm happy to gate this behind a new PluginOptions flag (default off) so workspace-package response types are only referenced when explicitly enabled — just let me know.

Testing. Added a #4006-shaped case to normalize-package-path.spec.ts covering the specifier normalization. The transpile-based plugin unit harness can't reproduce a real pnpm node_modules symlink layout, so end-to-end behavior was verified manually against the reporter's minimal reproduction (<PASTE GIST URL FROM #4006>): the generated controller emits the response type as expected. I can follow up with a runtime integration test that materializes a physical node_modules/@scope/pkg in a temp dir and asserts the emitted type, if committed end-to-end coverage is preferred.

…ackages

The controller visitor bailed whenever the inferred response type resolved
through node_modules (e.g. a pnpm-symlinked workspace package), emitting
ApiResponse without a type and leaving the operation with no response schema.
Remove that guard so control falls through to the existing
typeReferenceToIdentifier -> replaceImportPath -> normalizePackagePath chain,
aligning the controller response path with the model/property path (nestjs#3798).

Fixes nestjs#4006
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.

CLI plugin drops inferred response models imported from a workspace package

1 participant