Skip to content

Conversation

@unnoq
Copy link
Member

@unnoq unnoq commented Nov 27, 2025

  • custom send response
  • global context
  • plugins
  • error filter
  • custom error response
  • tests
  • docs

Summary by CodeRabbit

  • New Features

    • Custom response interceptors for overriding response body and headers
    • Dynamic per-route success status support
    • Async module configuration that injects the request into handler context
    • Configurable JSON/serializer options and plugin support
  • Documentation

    • Updated Nest integration guide with an advanced "Custom Send Response" example
  • Tests

    • Expanded tests for dynamic statuses, interceptors, serializers, and compatibility scenarios

✏️ Tip: You can customize this high-level summary in your review settings.

@unnoq unnoq requested a review from Copilot November 27, 2025 09:44
@dosubot dosubot bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Nov 27, 2025
@coderabbitai
Copy link

coderabbitai bot commented Nov 27, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds ORPCGlobalContext.request typing, expands ORPCModule.forRootAsync config with serializers/plugins/interceptors, moves per-instance codec initialization into ImplementInterceptor, applies per-route HttpCode, and routes final responses through configurable sendResponseInterceptors; docs, tests, and a local implement wrapper updated.

Changes

Cohort / File(s) Summary
Documentation
apps/content/docs/openapi/integrations/implement-contract-in-nest.md
Adds module augmentation example for request, documents customJsonSerializers and plugins options, and adds an advanced "Custom Send Response" example showing interceptor-based body/header handling.
Implement & Interceptor
packages/nest/src/implement.ts
Implement now returns Router<T, ORPCGlobalContext>, applies HttpCode from route successStatus, accepts per-instance ORPCModuleConfig, initializes a per-instance StandardOpenAPICodec, and dispatches final emission via sendResponseInterceptors pipeline.
Module Types & Config
packages/nest/src/module.ts
Exports ORPCGlobalContext; extends ORPCModuleConfig with serializer option types, plugins, and sendResponseInterceptors; adapts CreateProcedureClientOptions usage to ORPCGlobalContext.
Index / Public API
packages/nest/src/index.ts
Removes re-export of implement from @orpc/server; adds a local implement wrapper that defaults context to ORPCGlobalContext and delegates to baseImplement.
Tests
packages/nest/src/implement.test.ts, packages/nest/src/index.test.ts
Expands tests: vitest scaffolding, sets nested.peng successStatus to 202, adds dynamic-status override test (203), tests for sendResponseInterceptors and custom serializers, Fastify cookie compatibility, and index export alias unit test.
Playground / Example
playgrounds/nest/src/app.module.ts, playgrounds/nest/package.json
Switches example to ORPCModule.forRootAsync with REQUEST injected into ORPCGlobalContext.request; adds @orpc/json-schema devDependency.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant NestHandler as Nest Route Handler
    participant ImplementInt as ImplementInterceptor
    participant Codec as StandardOpenAPICodec
    participant Interceptors as sendResponseInterceptors
    participant Sender as Fastify/Node Sender

    Client->>NestHandler: HTTP request
    NestHandler->>ImplementInt: invoke handler wrapper (with ORPCGlobalContext.request)
    ImplementInt->>Codec: init per-instance codec (config, serializers)
    ImplementInt->>NestHandler: call handler / await result
    NestHandler-->>ImplementInt: handler result (body)
    ImplementInt->>Codec: encode -> StandardResponse (status, headers, body)
    ImplementInt->>Interceptors: run sendResponseInterceptors pipeline
    Interceptors-->>ImplementInt: possibly-modified StandardResponse
    ImplementInt->>Sender: sendStandardResponse (Fastify or Node)
    Sender->>Client: HTTP response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas needing attention:
    • Correct mapping and application of per-route successStatus via HttpCode.
    • Per-instance codec initialization and serializer option propagation.
    • sendResponseInterceptors chaining semantics and Fastify vs Node send compatibility.
    • Types and public API changes in index.ts and ORPCGlobalContext augmentation.

Possibly related PRs

  • unnoq/orpc#710 — Overlaps module-level configuration and wiring into ImplementInterceptor and module types.
  • unnoq/orpc#480 — Related refactors to the Nest integration affecting Implement/ImplementInterceptor and context typing.
  • unnoq/orpc#916 — Adds async configuration and request injection into ORPC context; ties to forRootAsync usage and ORPCGlobalContext augmentation.

Poem

🐇 I nibble bytes and hop through code,

a Request tucked warm inside my tote,
serializers spin and interceptors sing,
statuses hop out on a dynamic spring,
little rabbit cheers — responses go! 🥕

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main features added in this PR: custom send response interceptors, global context support, plugins integration, and custom error response handling.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/nest/custom-send-response-behavior

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.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @unnoq, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the flexibility of the oRPC NestJS integration by introducing a mechanism for custom HTTP response handling. It allows developers to define interceptors that can modify or completely take over the process of sending responses, which is crucial for compatibility with certain NestJS features or third-party libraries. The changes also enable dynamic success status codes per route and configurable JSON serialization, providing more granular control over API behavior.

Highlights

  • Custom Response Sending: Introduced sendResponseInterceptors in ORPCModuleConfig to allow developers to customize or override the default HTTP response sending behavior, enabling better integration with NestJS features or external libraries.
  • Dynamic Success Status: Added the ability to define a successStatus directly within the oRPC contract route, which is then applied as the HTTP status code by NestJS using the @HttpCode decorator.
  • Configurable JSON Serializers: ORPCModuleConfig now supports customJsonSerializers, allowing for custom serialization logic to be applied to JSON responses.
  • Enhanced Documentation: Updated the OpenAPI integration documentation with an "Advanced" section detailing how to implement custom response sending.
  • Comprehensive Testing: Added new test cases to validate the dynamic success status, custom JSON serializers, and the functionality of sendResponseInterceptors.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a valuable feature for customizing response sending behavior in the NestJS integration through sendResponseInterceptors, and also adds support for custom JSON serializers. The changes are well-documented and accompanied by thorough tests. I've identified a minor performance improvement opportunity in the implementation of the ImplementInterceptor which I've detailed in a specific comment.

@codecov
Copy link

codecov bot commented Nov 27, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@pkg-pr-new
Copy link

pkg-pr-new bot commented Nov 27, 2025

More templates

@orpc/ai-sdk

npm i https://pkg.pr.new/@orpc/ai-sdk@1256

@orpc/arktype

npm i https://pkg.pr.new/@orpc/arktype@1256

@orpc/client

npm i https://pkg.pr.new/@orpc/client@1256

@orpc/contract

npm i https://pkg.pr.new/@orpc/contract@1256

@orpc/experimental-durable-iterator

npm i https://pkg.pr.new/@orpc/experimental-durable-iterator@1256

@orpc/hey-api

npm i https://pkg.pr.new/@orpc/hey-api@1256

@orpc/interop

npm i https://pkg.pr.new/@orpc/interop@1256

@orpc/json-schema

npm i https://pkg.pr.new/@orpc/json-schema@1256

@orpc/nest

npm i https://pkg.pr.new/@orpc/nest@1256

@orpc/openapi

npm i https://pkg.pr.new/@orpc/openapi@1256

@orpc/openapi-client

npm i https://pkg.pr.new/@orpc/openapi-client@1256

@orpc/otel

npm i https://pkg.pr.new/@orpc/otel@1256

@orpc/experimental-pino

npm i https://pkg.pr.new/@orpc/experimental-pino@1256

@orpc/experimental-publisher

npm i https://pkg.pr.new/@orpc/experimental-publisher@1256

@orpc/experimental-publisher-durable-object

npm i https://pkg.pr.new/@orpc/experimental-publisher-durable-object@1256

@orpc/experimental-ratelimit

npm i https://pkg.pr.new/@orpc/experimental-ratelimit@1256

@orpc/react

npm i https://pkg.pr.new/@orpc/react@1256

@orpc/react-query

npm i https://pkg.pr.new/@orpc/react-query@1256

@orpc/experimental-react-swr

npm i https://pkg.pr.new/@orpc/experimental-react-swr@1256

@orpc/server

npm i https://pkg.pr.new/@orpc/server@1256

@orpc/shared

npm i https://pkg.pr.new/@orpc/shared@1256

@orpc/solid-query

npm i https://pkg.pr.new/@orpc/solid-query@1256

@orpc/standard-server

npm i https://pkg.pr.new/@orpc/standard-server@1256

@orpc/standard-server-aws-lambda

npm i https://pkg.pr.new/@orpc/standard-server-aws-lambda@1256

@orpc/standard-server-fastify

npm i https://pkg.pr.new/@orpc/standard-server-fastify@1256

@orpc/standard-server-fetch

npm i https://pkg.pr.new/@orpc/standard-server-fetch@1256

@orpc/standard-server-node

npm i https://pkg.pr.new/@orpc/standard-server-node@1256

@orpc/standard-server-peer

npm i https://pkg.pr.new/@orpc/standard-server-peer@1256

@orpc/svelte-query

npm i https://pkg.pr.new/@orpc/svelte-query@1256

@orpc/tanstack-query

npm i https://pkg.pr.new/@orpc/tanstack-query@1256

@orpc/trpc

npm i https://pkg.pr.new/@orpc/trpc@1256

@orpc/valibot

npm i https://pkg.pr.new/@orpc/valibot@1256

@orpc/vue-colada

npm i https://pkg.pr.new/@orpc/vue-colada@1256

@orpc/vue-query

npm i https://pkg.pr.new/@orpc/vue-query@1256

@orpc/zod

npm i https://pkg.pr.new/@orpc/zod@1256

commit: 0ec130a

Copy link

@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: 1

🧹 Nitpick comments (1)
packages/nest/src/implement.ts (1)

107-112: Consider caching the codec instance.

The StandardOpenAPICodec is instantiated on every request inside the intercept method. If this.config is immutable after construction, this codec could be created once in the constructor and reused, avoiding repeated allocations.

 @Injectable()
 export class ImplementInterceptor implements NestInterceptor {
   private readonly config: ORPCModuleConfig
+  private readonly codec: StandardOpenAPICodec

   constructor(
     @Inject(ORPC_MODULE_CONFIG_SYMBOL) @Optional() config: ORPCModuleConfig | undefined,
   ) {
     // @Optional() does not allow set default value so we need to do it here
     this.config = config ?? {}
+    this.codec = new StandardOpenAPICodec(
+      new StandardOpenAPISerializer(
+        new StandardOpenAPIJsonSerializer(this.config),
+        new StandardBracketNotationSerializer(this.config),
+      ),
+    )
   }

   intercept(ctx: ExecutionContext, next: CallHandler<any>): Observable<any> {
-    const codec = new StandardOpenAPICodec(
-      new StandardOpenAPISerializer(
-        new StandardOpenAPIJsonSerializer(this.config),
-        new StandardBracketNotationSerializer(this.config),
-      ),
-    )
-
     return next.handle().pipe(

Then replace codec references with this.codec in the method body.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eccbaa4 and 315eccc.

📒 Files selected for processing (4)
  • apps/content/docs/openapi/integrations/implement-contract-in-nest.md (3 hunks)
  • packages/nest/src/implement.test.ts (5 hunks)
  • packages/nest/src/implement.ts (4 hunks)
  • packages/nest/src/module.ts (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
packages/nest/src/implement.test.ts (2)
packages/nest/src/implement.ts (1)
  • Implement (39-92)
packages/nest/src/index.ts (2)
  • Implement (2-2)
  • implement (6-6)
packages/nest/src/implement.ts (3)
packages/contract/src/config.ts (1)
  • fallbackContractConfig (20-26)
packages/nest/src/module.ts (2)
  • ORPCModuleConfig (13-22)
  • ORPC_MODULE_CONFIG_SYMBOL (11-11)
packages/shared/src/array.ts (1)
  • toArray (1-3)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Agent
  • GitHub Check: publish-commit
  • GitHub Check: test
  • GitHub Check: lint
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (8)
apps/content/docs/openapi/integrations/implement-contract-in-nest.md (1)

293-315: Solid example for the custom send response pattern.

The interceptor logic correctly demonstrates:

  • Validating response status and body type before custom handling
  • Falling back to default behavior via next() for non-object/array responses
  • Setting status and headers manually on the Express response
packages/nest/src/module.ts (1)

13-22: Configuration interface properly extended for new features.

The ORPCModuleConfig interface correctly extends the serializer option types and adds the sendResponseInterceptors field. Using any for request and response is reasonable here since NestJS supports multiple HTTP adapters (Express, Fastify).

packages/nest/src/implement.test.ts (3)

217-226: Good coverage for dynamic success status.

This test verifies that handlers can override the default/contract-defined status by returning a custom status in the response object. The as any cast is acceptable here for test mocking purposes.


493-552: Comprehensive tests for sendResponseInterceptors.

Both test cases effectively cover:

  1. Overriding response body while preserving the contract-defined status (202)
  2. Overriding both status and body via the response object

555-591: Useful compatibility test for Fastify cookie integration.

This test validates that oRPC plays nicely with Fastify plugins like @fastify/cookie when using @Res({ passthrough: true }).

packages/nest/src/implement.ts (3)

58-64: Correct application of HttpCode decorator for contract-defined status.

The HttpCode decorator is properly applied using fallbackContractConfig to ensure the contract's successStatus is respected. This integrates well with NestJS's response handling.


160-171: Clean interceptor pipeline for response handling.

The use of intercept with toArray elegantly handles both undefined and array cases for sendResponseInterceptors. The callback correctly distinguishes between Fastify and Node/Express responses using the 'raw' in response check.


96-104: Good defensive handling for optional config injection.

The comment explaining why manual defaulting is needed with @Optional() is helpful. The nullish coalescing to an empty object ensures safe property access throughout the class.

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Nov 27, 2025

Deploying orpc with  Cloudflare Pages  Cloudflare Pages

Latest commit: 0ec130a
Status: ✅  Deploy successful!
Preview URL: https://e02bbb29.orpc-1qh.pages.dev
Branch Preview URL: https://feat-nest-custom-send-respon.orpc-1qh.pages.dev

View logs

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request adds custom send response behavior to the @orpc/nest package, allowing users to intercept and customize how responses are sent to clients. This enables better compatibility with NestJS features and third-party libraries that rely on the standard NestJS response handling pattern.

Key changes:

  • Introduced sendResponseInterceptors configuration option to allow custom response handling logic
  • Added support for custom JSON and bracket notation serializers through extended configuration options
  • Applied HttpCode decorator to respect contract-defined success status codes

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
packages/nest/src/module.ts Extended ORPCModuleConfig interface with sendResponseInterceptors and serializer options
packages/nest/src/implement.ts Moved codec instantiation to request-time, added HttpCode decorator for success status, integrated sendResponseInterceptors with intercept mechanism
packages/nest/src/implement.test.ts Added comprehensive tests for sendResponseInterceptors and custom serializers, reorganized tests into logical describe blocks
apps/content/docs/openapi/integrations/implement-contract-in-nest.md Added documentation for custom send response feature with example implementation, updated configuration examples to show serializer options

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@unnoq unnoq changed the title feat(nest): custom send response behavior feat(nest): custom send response, plugins, error filter Nov 27, 2025
@unnoq unnoq marked this pull request as draft November 27, 2025 12:28
@unnoq unnoq changed the title feat(nest): custom send response, plugins, error filter feat(nest): custom send response, global context, plugins, error filter Nov 27, 2025
@unnoq unnoq changed the title feat(nest): custom send response, global context, plugins, error filter feat(nest): custom send response, global context, plugins, custom error response Nov 27, 2025
@unnoq unnoq marked this pull request as ready for review November 27, 2025 13:55
@unnoq unnoq requested a review from Copilot November 27, 2025 13:55
@dosubot dosubot bot added size:XL This PR changes 500-999 lines, ignoring generated files. and removed size:L This PR changes 100-499 lines, ignoring generated files. labels Nov 27, 2025
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 9 changed files in this pull request and generated 4 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

@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: 1

♻️ Duplicate comments (2)
apps/content/docs/openapi/integrations/implement-contract-in-nest.md (1)

295-323: Missing Response import from express.

Line 315 casts response as Response, but line 297 only imports Request from express. This will cause a TypeScript error when users copy this example.

-import { Request } from 'express' // if you use express adapter
+import { Request, Response } from 'express' // if you use express adapter
packages/nest/src/implement.test.ts (1)

493-552: Missing test for next() fallback in sendResponseInterceptors.

The current tests cover overriding responses but don't verify that calling next() within a sendResponseInterceptor correctly falls back to the default sendStandardResponse behavior. Consider adding a test that invokes next() to ensure the interceptor pipeline works as documented.

it('can call next() to fallback to default behavior', async () => {
  const interceptor = vi.fn(({ next }) => next())
  const moduleRef = await Test.createTestingModule({
    imports: [
      ORPCModule.forRoot({
        sendResponseInterceptors: [interceptor],
      }),
    ],
    controllers: [ImplProcedureController],
  }).compile()

  const app = moduleRef.createNestApplication()
  await app.init()

  const httpServer = app.getHttpServer()
  const res = await supertest(httpServer).delete('/world/who%3F')

  expect(res.statusCode).toEqual(202)
  expect(res.body).toEqual('peng world/who?')
  expect(interceptor).toHaveBeenCalledTimes(1)
  expect(sendStandardResponseSpy).toHaveBeenCalledTimes(1)
})
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 315eccc and 0a6f7cb.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (8)
  • apps/content/docs/openapi/integrations/implement-contract-in-nest.md (3 hunks)
  • packages/nest/src/implement.test.ts (5 hunks)
  • packages/nest/src/implement.ts (5 hunks)
  • packages/nest/src/index.test.ts (1 hunks)
  • packages/nest/src/index.ts (2 hunks)
  • packages/nest/src/module.ts (1 hunks)
  • playgrounds/nest/package.json (1 hunks)
  • playgrounds/nest/src/app.module.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
packages/nest/src/index.test.ts (1)
packages/nest/src/index.ts (1)
  • implement (25-30)
playgrounds/nest/src/app.module.ts (1)
packages/nest/src/module.ts (2)
  • ORPCGlobalContext (26-28)
  • Module (44-81)
packages/nest/src/implement.test.ts (1)
packages/nest/src/implement.ts (1)
  • Implement (37-90)
🪛 Biome (2.1.2)
packages/nest/src/module.ts

[error] 26-28: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: CodeQL analysis (javascript-typescript)
  • GitHub Check: Agent
  • GitHub Check: lint
  • GitHub Check: test
  • GitHub Check: publish-commit
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (15)
playgrounds/nest/package.json (1)

18-18: LGTM!

The new @orpc/json-schema dependency is required for the SmartCoercionPlugin used in app.module.ts, and the version tag is consistent with other @orpc/* packages.

packages/nest/src/index.test.ts (1)

1-21: LGTM!

The test correctly verifies that the implement alias delegates to @orpc/server's implementation with the correct arguments and returns the expected value. The mock setup properly wraps the original function with a spy.

apps/content/docs/openapi/integrations/implement-contract-in-nest.md (1)

227-254: LGTM!

The ORPCGlobalContext type augmentation example and the updated configuration options (customJsonSerializers, plugins) are well-documented and align with the implementation in app.module.ts.

playgrounds/nest/src/app.module.ts (1)

24-43: LGTM!

The async module configuration correctly injects the request context and sets up the SmartCoercionPlugin with ZodToJsonSchemaConverter. This demonstrates the new forRootAsync pattern well.

packages/nest/src/index.ts (2)

22-30: LGTM!

The implement alias with ORPCGlobalContext as the default context type provides good developer experience for Nest users by eliminating the need to explicitly specify context types. The implementation correctly delegates to the base implementation while preserving generic flexibility.


1-11: LGTM!

Clean separation of imports and explicit re-exports. Removing the blanket re-export of implement from @orpc/server in favor of the custom alias ensures users get the Nest-specific version with proper defaults.

packages/nest/src/implement.test.ts (3)

217-226: Good addition of dynamic success status test.

This test properly validates that handlers can override the default status code at runtime, ensuring the status property in the response is respected.


554-641: Good test for plugin cloning behavior.

The test correctly verifies that handler options are cloned before applying plugins, preventing interceptors from running multiple times across requests. The multiple request assertions (res1, res2, res3) effectively validate this behavior.


673-709: Good compatibility test for Fastify cookie handling.

This test ensures that the interceptor flow properly integrates with Fastify plugins like @fastify/cookie, validating that response modifications (setting cookies) work alongside oRPC response handling.

packages/nest/src/module.ts (2)

15-28: Empty interface is intentional for module augmentation.

The static analysis hint about empty interface is a false positive in this context. The ORPCGlobalContext interface is intentionally empty to serve as a declaration merging point, allowing users to extend it via module augmentation as documented in the JSDoc. This is a common TypeScript pattern for extensible type systems.


30-42: Well-structured module configuration types.

The extended ORPCModuleConfig properly combines multiple option interfaces and adds the new plugins and sendResponseInterceptors configurations with appropriate typing.

packages/nest/src/implement.ts (4)

96-111: Good refactor: codec now instantiated per-instance.

The StandardOpenAPICodec is now created once in the constructor and stored as a class property, avoiding unnecessary object instantiation on every request. This addresses the previous review feedback.


132-140: Good defensive cloning of arrays before plugin initialization.

The comment explains the rationale well. Cloning interceptors and plugins arrays prevents plugins from mutating shared state across requests. Consider tracking this TODO for future improvement.


147-158: Well-designed interceptor pipeline for response handling.

The intercept utility with sendResponseInterceptors provides a clean extension point for customizing response behavior while maintaining proper support for both Fastify and Node.js response types.


146-159: Based on my verification of the codebase, I can now provide a comprehensive rewritten review comment:

Handle unmatched routes explicitly instead of returning undefined.

The current code implicitly returns undefined when result.matched is false. While this shouldn't occur in normal operation (the hardcoded match callback always succeeds and no prefix is configured), other adapters like FastifyHandler and NodeHttpHandler explicitly return or propagate the unmatched status. For consistency and defensive programming, add explicit handling:

if (result.matched) {
  return intercept(
    toArray(this.config.sendResponseInterceptors),
    { request: req, response: res, standardResponse: result.response },
    async ({ response, standardResponse }) => {
      if ('raw' in response) {
        await StandardServerFastify.sendStandardResponse(response, standardResponse, this.config)
      }
      else {
        await StandardServerNode.sendStandardResponse(response, standardResponse, this.config)
      }
    },
  )
}

// Handle unmatched case explicitly
throw new Error(`Route not matched for ${req.method} ${req.url}`)

This prevents silent failures if the route configuration changes in the future.

Copy link

@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: 0

🧹 Nitpick comments (1)
packages/nest/src/module.ts (1)

29-29: Track the TODO for future refactoring.

The TODO suggests replacing CreateProcedureClientOptions with StandardHandlerOptions. Consider tracking this as a follow-up task to ensure it's addressed.

Would you like me to open a new issue to track this refactoring task?

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0a6f7cb and f58dd47.

📒 Files selected for processing (1)
  • packages/nest/src/module.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/nest/src/module.ts (7)
packages/server/src/procedure-client.ts (1)
  • CreateProcedureClientOptions (43-65)
packages/openapi-client/src/adapters/standard/openapi-json-serializer.ts (1)
  • StandardOpenAPIJsonSerializerOptions (10-12)
packages/openapi-client/src/adapters/standard/bracket-notation.ts (1)
  • StandardBracketNotationSerializerOptions (6-21)
packages/openapi/src/adapters/standard/openapi-codec.ts (1)
  • StandardOpenAPICodecOptions (10-21)
packages/server/src/adapters/standard/handler.ts (1)
  • StandardHandlerOptions (27-48)
packages/shared/src/interceptor.ts (1)
  • Interceptor (13-16)
packages/standard-server/src/types.ts (1)
  • StandardResponse (34-41)
🪛 Biome (2.1.2)
packages/nest/src/module.ts

[error] 26-28: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: lint
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (3)
packages/nest/src/module.ts (3)

3-8: LGTM!

The new imports correctly support the extended configuration options for serializers, codecs, plugins, and response interceptors.


15-28: LGTM! The empty interface pattern is intentional.

This follows the standard TypeScript module augmentation pattern, allowing users to extend the global context via declaration merging. The Biome lint warning can be safely ignored—empty interfaces are the idiomatic approach for this use case.


30-42: Perfect! I now have enough information to verify the review comment. Let me check one more thing about how the interceptor is actually called to ensure the any typing is truly necessary:

#!/bin/bash

Look at the actual implementation of how sendResponseInterceptors is called

sed -n '145,155p' packages/nest/src/implement.ts

Based on my investigation, I have verified all the necessary information:

Key Findings:

  1. File context: packages/nest/src/implement.ts imports both Request, Response from 'express' and FastifyReply, FastifyRequest from 'fastify'
  2. Actual usage (line 149): The interceptor receives { request: req, response: res, standardResponse: result.response } where req and res are the actual Nest.js handler parameters
  3. Platform agnosticity: The module is designed to support multiple HTTP platforms (Express, Fastify)
  4. Nest.js pattern: @nestjs/common provides Req and Res decorators but does not export a single typed Request/Response interface because it's platform-agnostic

Conclusion: The any typing is intentional and justified. Nest.js handlers work with platform-specific request/response objects (Express.Request vs Fastify.Request), and there is no single type that covers both. Using any is the pragmatic and correct approach for framework-agnostic module configuration.


The any types for request and response in sendResponseInterceptors are justified and necessary for Nest.js platform agnosticity.

The module intentionally supports multiple HTTP platforms (Express and Fastify), which have incompatible Request/Response types. Nest.js does not provide a unified type abstraction for these, so any is the appropriate choice here. No changes needed.

Copy link

@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: 1

♻️ Duplicate comments (2)
apps/content/docs/openapi/integrations/implement-contract-in-nest.md (1)

239-252: Critical: Same REQUEST injection issue as in app.module.ts.

This documentation example demonstrates the same problematic pattern of injecting REQUEST into forRootAsync's factory. As noted in the review of playgrounds/nest/src/app.module.ts, this may not work as intended because the factory is called once during module initialization, not per-request.

If the pattern in the playground is incorrect, this documentation would teach users an incorrect approach.

The verification script in the app.module.ts review will confirm whether this pattern is valid.

playgrounds/nest/src/app.module.ts (1)

41-41: Fix typo: "almost" should be "most".

Apply this diff:

-        ], // almost oRPC plugins are compatible
+        ], // most oRPC plugins are compatible
🧹 Nitpick comments (1)
apps/content/docs/openapi/integrations/implement-contract-in-nest.md (1)

290-332: LGTM! Well-documented custom send response pattern.

The "Advanced: Custom Send Response" example is well-structured and clearly demonstrates how to customize response handling. All necessary imports are present (including Response from Express, addressing previous review comments), and the conditional logic correctly falls back to default behavior for non-2xx responses or non-object/array bodies.

Optional: Consider De Morgan's law for clarity.

Line 310's double negation could be slightly clearer:

-            || !(isObject(standardResponse.body) || Array.isArray(standardResponse.body))
+            || (!isObject(standardResponse.body) && !Array.isArray(standardResponse.body))

Both forms are logically equivalent, but the second may be more readable.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f58dd47 and 0ec130a.

📒 Files selected for processing (2)
  • apps/content/docs/openapi/integrations/implement-contract-in-nest.md (3 hunks)
  • playgrounds/nest/src/app.module.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
playgrounds/nest/src/app.module.ts (1)
packages/nest/src/module.ts (2)
  • ORPCGlobalContext (26-28)
  • Module (44-81)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: publish-commit
  • GitHub Check: lint
  • GitHub Check: test
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (1)
apps/content/docs/openapi/integrations/implement-contract-in-nest.md (1)

223-236: LGTM!

All necessary imports are present, and the module augmentation correctly extends ORPCGlobalContext with type-safe request property. This addresses previous review comments about missing imports.

@unnoq unnoq merged commit 56695f3 into main Nov 30, 2025
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants