Skip to content

feat(server): native Fastify adapter#1127

Merged
dinwwwh merged 11 commits intomainfrom
feat/server/native-fastify-adapter
Oct 26, 2025
Merged

feat(server): native Fastify adapter#1127
dinwwwh merged 11 commits intomainfrom
feat/server/native-fastify-adapter

Conversation

@dinwwwh
Copy link
Copy Markdown
Member

@dinwwwh dinwwwh commented Oct 23, 2025

This PR adds a native oRPC adapter for Fastify. Previously, oRPC in Fastify used the Node adapter, which didn't integrate well with Fastify's ecosystem (e.g., cookies, helpers, middleware). This native adapter supports Fastify's request/reply APIs directly, enabling full access to Fastify features within oRPC.

Closes: #998, #992

Summary by CodeRabbit

  • New Features

    • First-class Fastify support across server, OpenAPI, and standard-server packages (Fastify adapters, RPC and OpenAPI handlers)
    • API configuration now prefers interceptor-based error handling instead of plugin arrays
  • Documentation

    • Added Fastify adapter docs, examples, and a README; updated Nest guidance to note Fastify considerations
  • Tests

    • New Fastify-focused unit and integration tests (including cookie handling and OpenAPI scenarios)
  • Chores

    • Package exports and manifests updated to publish Fastify adapters and wiring

@vercel
Copy link
Copy Markdown

vercel bot commented Oct 23, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
orpc Ready Ready Preview Comment Oct 26, 2025 8:07am

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Oct 23, 2025

Walkthrough

Adds first-class Fastify support and documentation: new @orpc/standard-server-fastify package (request/response adapters), Fastify handler and RPCHandler in @orpc/server, OpenAPI Fastify adapter, docs/examples updated, Nest integration adjusted, tests and package export entries added.

Changes

Cohort / File(s) Summary
Docs: Fastify examples & refs
apps/content/docs/adapters/fastify.md, apps/content/docs/adapters/http.md, apps/content/openapi/integrations/implement-contract-in-nest.md, apps/content/learn-and-contribute/overview.md
Replace node-side Fastify example with Fastify-centric RPCHandler + interceptor usage; add Fastify to adapters table; remove NestJS experimental warning; add standard-server-fastify link.
Standard-server-fastify package
packages/standard-server-fastify/package.json, packages/standard-server-fastify/tsconfig.json, packages/standard-server-fastify/README.md, packages/standard-server-fastify/.gitignore, packages/standard-server-fastify/src/index.ts, packages/standard-server-fastify/src/request.ts, packages/standard-server-fastify/src/response.ts, packages/standard-server-fastify/src/*.test.ts
New package exporting toStandardLazyRequest, sendStandardResponse, Fastify types, tests, build config, and package metadata.
Server: Fastify adapter implementation
packages/server/src/adapters/fastify/handler.ts, packages/server/src/adapters/fastify/rpc-handler.ts, packages/server/src/adapters/fastify/index.ts
Add FastifyHandler (interceptor-aware) and RPCHandler (wraps StandardRPCHandler, enables StrictGetMethodPlugin by default), plus index re-exports.
Server: Fastify adapter tests & types
packages/server/src/adapters/fastify/*.test.ts, packages/server/src/adapters/fastify/handler.test-d.ts
Unit and type tests covering handle match/mismatch flows, interceptor invocation, RPC integration, and exported symbols.
OpenAPI: Fastify adapter
packages/openapi/src/adapters/fastify/openapi-handler.ts, packages/openapi/src/adapters/fastify/index.ts, packages/openapi/src/adapters/fastify/*.test.ts, packages/openapi/package.json
New OpenAPIHandler wrapping StandardOpenAPIHandler for Fastify, index re-export, tests, and package export/devDependency additions.
Nest integration changes
packages/nest/src/implement.ts, packages/nest/src/implement.test.ts, packages/nest/package.json
ImplementInterceptor dispatches to Fastify vs Node standard helpers; add Fastify cookie integration test; update package deps.
Monorepo package exports & deps
packages/server/package.json, packages/openapi/package.json, packages/nest/package.json, packages/standard-server-fastify/package.json
Add ./fastify exports for server/openapi, add workspace dependency on @orpc/standard-server-fastify, add devDeps (fastify, @fastify/cookie).
Node adapter/type & test tweaks
packages/server/src/adapters/node/handler.ts, packages/server/src/adapters/node/handler.test.ts, packages/standard-server-node/src/response.test.ts
Remove implements clause from Node handler class (type change) and tighten tests for mismatch result shape and explicit content-type expectations.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Client
    participant Fastify
    participant FastifyHandler
    participant Interceptor
    participant StandardHandler
    participant ResponseSender as sendStandardResponse

    Client->>Fastify: HTTP request
    Fastify->>FastifyHandler: handle(req, reply, { prefix, ... })
    FastifyHandler->>Interceptor: intercept({ request, reply, sendStandardResponseOptions }, next)
    Interceptor->>FastifyHandler: next()
    FastifyHandler->>StandardHandler: handle(standardRequest, options)
    alt matched
        StandardHandler-->>FastifyHandler: { matched: true, response }
        FastifyHandler->>ResponseSender: sendStandardResponse(reply, response, opts)
        ResponseSender->>Fastify: set headers + send(body)
    else not matched
        StandardHandler-->>FastifyHandler: { matched: false }
        FastifyHandler-->>Fastify: ({ matched: false })
    end
    Fastify-->Client: HTTP response
Loading
sequenceDiagram
    autonumber
    participant FastifyReq as FastifyRequest
    participant toStandardLazyRequest
    participant BodyResolver

    FastifyReq->>toStandardLazyRequest: (req, reply)
    toStandardLazyRequest->>BodyResolver: create lazy body getter (signal)
    alt req.body present
        BodyResolver-->>toStandardLazyRequest: return req.body
    else no parsed body
        BodyResolver->>BodyResolver: call toStandardBody(req.raw, { signal })
        BodyResolver-->>toStandardLazyRequest: parsed body
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Areas to focus review on:

  • Correct wiring of interceptors vs previous plugin model in rpc-handler.ts and handler.ts.
  • toStandardLazyRequest and sendStandardResponse correctness (signal handling, header propagation, streaming/error flows) in packages/standard-server-fastify/src/*.
  • Public exports and package.json export entries across packages/server and packages/openapi.
  • Tests asserting behavior for StrictGetMethodPlugin defaults and cookie integration.

Possibly related PRs

  • unnoq/orpc#1001 — implements preferring pre-parsed req.body when building the standard request; closely related to Fastify request parsing behavior.
  • unnoq/orpc#292 — adapter-level refactor switching plugins→interceptors; aligns with RPCHandler/options changes in this PR.
  • unnoq/orpc#176 — adds/wires a Fastify standard-server adapter and adapter exports; overlaps design and export surface.

Suggested labels

size:XL

Poem

🐰
I hopped through routes both swift and spry,
Wrapped requests and replies on the fly.
Interceptors whisper, responses hop back—
Fastify and rabbit keep latency slack! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Out of Scope Changes Check ❓ Inconclusive While the majority of changes directly support the Fastify adapter feature, there are modifications to the Node adapter that appear tangential to the primary scope. Specifically, packages/server/src/adapters/node/handler.ts was refactored to remove the implements NodeHttpHandler<T> clause from the class signature, and packages/server/src/adapters/node/handler.test.ts was updated to simplify the mock return value from { matched: false, response: undefined } to { matched: false }. These changes are not explicitly mentioned in the PR objectives and do not appear necessary to implement the Fastify adapter feature itself, though they may represent intentional API normalization across adapter implementations. Clarify whether the Node adapter refactoring (signature change and test updates) was intentional as part of API normalization across handlers or represents unplanned scope expansion. If it is intentional alignment work, update the PR description to document this as a supporting change. If unintended, these modifications should be reverted to a separate PR or removed entirely to maintain focus on the Fastify adapter implementation.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "feat(server): native Fastify adapter" directly captures the primary objective of the changeset. The title clearly summarizes the main contribution—adding native Fastify adapter support to the oRPC server—which is evident across the changes including the new standard-server-fastify package, FastifyHandler/RPCHandler/OpenAPIHandler classes, and related documentation updates. The title is concise, specific, and avoids generic phrasing or unnecessary details.
Linked Issues Check ✅ Passed The code changes comprehensively address the objectives from linked issues #998 and #992. For #998, the PR implements a FastifyHandler class (approach #2) that enables oRPC integration into existing Fastify servers, allowing Fastify middleware and ecosystem features (cookies, helpers) to apply to oRPC routes—directly solving the issue of middleware replication. The new standard-server-fastify package provides toStandardLazyRequest and sendStandardResponse utilities that bridge Fastify and oRPC's standard request/response models. For #992, the Nest.js integration (packages/nest/src/implement.ts) now conditionally routes between Fastify and Node frameworks, with tests validating cookie support. All major artifacts (standard server adapter, RPC adapter, OpenAPI adapter, and documentation) are present and implement the stated requirements.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/server/native-fastify-adapter

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3c539a7 and 6168414.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (3)
  • packages/nest/package.json (1 hunks)
  • packages/openapi/package.json (3 hunks)
  • packages/server/package.json (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/nest/package.json
  • packages/server/package.json
  • packages/openapi/package.json
⏰ 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: lint
  • GitHub Check: test
  • GitHub Check: publish-commit
  • GitHub Check: Analyze (javascript-typescript)

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.

@codecov
Copy link
Copy Markdown

codecov bot commented Oct 23, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Oct 23, 2025

More templates

@orpc/arktype

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

@orpc/client

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

@orpc/contract

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

@orpc/experimental-durable-iterator

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

@orpc/hey-api

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

@orpc/interop

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

@orpc/json-schema

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

@orpc/nest

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

@orpc/openapi

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

@orpc/openapi-client

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

@orpc/otel

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

@orpc/experimental-publisher

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

@orpc/react

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

@orpc/react-query

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

@orpc/experimental-react-swr

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

@orpc/server

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

@orpc/shared

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

@orpc/solid-query

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

@orpc/standard-server

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

@orpc/standard-server-aws-lambda

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

@orpc/standard-server-fastify

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

@orpc/standard-server-fetch

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

@orpc/standard-server-node

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

@orpc/standard-server-peer

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

@orpc/svelte-query

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

@orpc/tanstack-query

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

@orpc/trpc

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

@orpc/valibot

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

@orpc/vue-colada

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

@orpc/vue-query

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

@orpc/zod

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

commit: 6168414

@dinwwwh dinwwwh marked this pull request as ready for review October 23, 2025 09:29
@dosubot dosubot bot added the size:XXL This PR changes 1000+ lines, ignoring generated files. label Oct 23, 2025
@dinwwwh dinwwwh added the lgtm This PR has been approved by a maintainer label Oct 23, 2025
Copy link
Copy Markdown

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

🧹 Nitpick comments (16)
packages/standard-server-fastify/src/index.test.ts (1)

1-3: Consider adding functional tests beyond export verification.

This test only verifies that toStandardLazyRequest is exported, but doesn't validate its behavior, edge cases, or integration with Fastify request/reply objects.

Consider adding tests that:

  • Verify request method, URL, and headers are correctly mapped
  • Test body resolution with both parsed and raw bodies
  • Validate abort signal propagation
  • Check error handling in body parsing
packages/openapi/src/adapters/fastify/index.test.ts (1)

1-3: Consider adding functional tests for OpenAPIHandler.

Similar to the standard-server-fastify tests, this only verifies the export exists without testing actual OpenAPIHandler functionality for Fastify.

Consider adding integration tests that verify:

  • OpenAPI request handling with Fastify request/reply objects
  • Response serialization and status code handling
  • Error responses and validation failures
packages/server/src/adapters/fastify/index.test.ts (1)

1-3: Consider adding functional tests for RPCHandler.

This test only verifies the RPCHandler export without testing its integration with Fastify's request/reply handling or RPC protocol implementation.

Consider adding tests that verify:

  • RPC request handling with Fastify-specific features
  • Proper context propagation from Fastify request/reply
  • Error handling and response serialization
  • Integration with Fastify plugins and middleware
packages/openapi/src/adapters/fastify/openapi-handler.test.ts (1)

6-21: Consider expanding test coverage.

The test validates basic functionality, but the OpenAPI handler likely supports additional features worth testing:

  • Error handling and validation failures
  • Different HTTP methods and content types
  • OpenAPI-specific behaviors (schema validation, serialization)
  • Prefix edge cases (empty prefix, trailing slashes)
  • Non-matching routes
packages/server/src/adapters/fastify/rpc-handler.test.ts (1)

6-38: Consider expanding test coverage.

While the current tests validate core functionality, additional test cases would strengthen confidence:

  • POST requests to demonstrate non-GET method handling
  • Error scenarios (invalid routes, malformed requests)
  • Plugin configuration options (disabling StrictGetMethodPlugin)
  • Multiple prefix configurations
  • Content-type handling
packages/standard-server-fastify/README.md (5)

5-5: Remove empty H1 or give it a title.

Empty <h1></h1> hurts accessibility/SEO.

-<h1></h1>
+<!-- Removed: empty heading -->

46-49: Make the docs link descriptive (MD059).

Avoid “here” link text; use “oRPC Documentation”.

-You can find the full documentation [here](https://orpc.unnoq.com).
+See the [oRPC Documentation](https://orpc.unnoq.com).

73-76: Add alt text to the sponsors image (MD045).

-    <img src='https://cdn.jsdelivr.net/gh/unnoq/unnoq/sponsors.svg'/>
+    <img src='https://cdn.jsdelivr.net/gh/unnoq/unnoq/sponsors.svg' alt="oRPC sponsors"/>

44-44: Wording nit: “Extensibility” reads better than “Extendability”.

-- **🔌 Extendability**: Easily extend functionality with plugins, middleware, and interceptors.
+- **🔌 Extensibility**: Easily extend functionality with plugins, middleware, and interceptors.

25-25: Style nit: “Type‑safe” is more standard than “Typesafe”.

-<h3 align="center">Typesafe APIs Made Simple 🪄</h3>
+<h3 align="center">Type‑safe APIs Made Simple 🪄</h3>
packages/nest/src/implement.ts (1)

123-126: Compute framework once and use a consistent branch.

Branching on 'raw' in req and later 'raw' in res can diverge. Determine Fastify once and reuse to avoid mismatches and tighten the guard.

-        const standardRequest = 'raw' in req
-          ? StandardServerFastify.toStandardLazyRequest(req, res as FastifyReply)
-          : StandardServerNode.toStandardLazyRequest(req, res as Response)
+        const isFastify = typeof (req as any)?.raw?.method === 'string' && typeof (res as any)?.raw?.setHeader === 'function'
+        const standardRequest = isFastify
+          ? StandardServerFastify.toStandardLazyRequest(req as FastifyRequest, res as FastifyReply)
+          : StandardServerNode.toStandardLazyRequest(req as Request, res as Response)
@@
-        if ('raw' in res) {
+        if (isFastify) {
           await StandardServerFastify.sendStandardResponse(res, standardResponse, this.config)
         }
         else {
           await StandardServerNode.sendStandardResponse(res, standardResponse, this.config)
         }

Also applies to: 156-161

packages/standard-server-fastify/src/request.test.ts (2)

40-41: Avoid relying on raw.headers identity; assert Fastify headers instead.

req.headers is what the adapter returns; identity with raw.headers isn’t guaranteed.

-    expect(standardRequest.headers).toBe(fastifyReq.raw.headers)
+    expect(standardRequest.headers).toBe(fastifyReq.headers)
+    // or toStrictEqual if identity is not guaranteed by Fastify:
+    // expect(standardRequest.headers).toStrictEqual(fastifyReq.headers)

72-78: FormData deep equality is brittle; compare entries and assert fallback parser ran.

Strengthen the assertion and verify toStandardBody was used.

-    const expectedBody = new FormData()
-    expectedBody.append('foo', 'bar')
-    expect(standardBody).toEqual(expectedBody)
+    // ensure fallback parser was used
+    expect(toStandardBodySpy).toHaveBeenCalledTimes(1)
+    // compare entries to avoid implementation-specific equality issues
+    const entries = Array.from((standardBody as FormData).entries())
+    expect(entries).toEqual([['foo', 'bar']])
packages/standard-server-fastify/src/response.ts (1)

24-32: Consider simplifying the conditional body sending logic.

The else if branch for strings (lines 27-29) and the final else branch (lines 30-32) perform identical operations since Fastify's reply.send() handles both strings and Readable streams. This can be simplified for better maintainability.

Apply this diff to simplify:

     if (resBody === undefined) {
       reply.send()
     }
-    else if (typeof resBody === 'string') {
-      reply.send(resBody)
-    }
     else {
       reply.send(resBody)
     }
apps/content/docs/adapters/fastify.md (1)

10-12: Clarify the warning about content-type parsing.

The warning states that "oRPC will use the parsed body when available," but the example immediately below (lines 31-35) shows disabling Fastify's parsing by returning undefined. This creates potential confusion about when and why developers should disable parsing versus letting Fastify handle it.

Consider expanding the warning to clarify the tradeoff:

 ::: warning
-Fastify parses common request content types by default. oRPC will use the parsed body when available.
+Fastify parses common request content types by default. To fully utilize oRPC features (such as streaming inputs), you can disable Fastify's content parsing for oRPC routes by using a wildcard content-type parser that returns `undefined`, allowing oRPC to handle the raw request body. See the example below.
 :::
packages/server/src/adapters/fastify/rpc-handler.ts (1)

25-32: Avoid mutating the input options object.

The constructor directly mutates the options parameter (lines 27-28), which can lead to unexpected behavior if the user reuses the same options object for multiple handler instances or keeps a reference to it.

Apply this diff to avoid mutation:

   constructor(router: Router<any, T>, options: NoInfer<RPCHandlerOptions<T>> = {}) {
-    if (options.strictGetMethodPluginEnabled ?? true) {
-      options.plugins ??= []
-      options.plugins.push(new StrictGetMethodPlugin())
-    }
-
-    super(new StandardRPCHandler(router, options), options)
+    const handlerOptions = {
+      ...options,
+      plugins: options.strictGetMethodPluginEnabled ?? true
+        ? [...(options.plugins ?? []), new StrictGetMethodPlugin()]
+        : options.plugins,
+    }
+
+    super(new StandardRPCHandler(router, handlerOptions), handlerOptions)
   }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6a2fc81 and 20bd292.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (30)
  • apps/content/docs/adapters/fastify.md (1 hunks)
  • apps/content/docs/adapters/http.md (2 hunks)
  • apps/content/docs/openapi/integrations/implement-contract-in-nest.md (0 hunks)
  • apps/content/learn-and-contribute/overview.md (1 hunks)
  • packages/nest/package.json (1 hunks)
  • packages/nest/src/implement.test.ts (2 hunks)
  • packages/nest/src/implement.ts (3 hunks)
  • packages/openapi/package.json (3 hunks)
  • packages/openapi/src/adapters/fastify/index.test.ts (1 hunks)
  • packages/openapi/src/adapters/fastify/index.ts (1 hunks)
  • packages/openapi/src/adapters/fastify/openapi-handler.test.ts (1 hunks)
  • packages/openapi/src/adapters/fastify/openapi-handler.ts (1 hunks)
  • packages/server/package.json (4 hunks)
  • packages/server/src/adapters/fastify/handler.test-d.ts (1 hunks)
  • packages/server/src/adapters/fastify/handler.test.ts (1 hunks)
  • packages/server/src/adapters/fastify/handler.ts (1 hunks)
  • packages/server/src/adapters/fastify/index.test.ts (1 hunks)
  • packages/server/src/adapters/fastify/index.ts (1 hunks)
  • packages/server/src/adapters/fastify/rpc-handler.test.ts (1 hunks)
  • packages/server/src/adapters/fastify/rpc-handler.ts (1 hunks)
  • packages/standard-server-fastify/.gitignore (1 hunks)
  • packages/standard-server-fastify/README.md (1 hunks)
  • packages/standard-server-fastify/package.json (1 hunks)
  • packages/standard-server-fastify/src/index.test.ts (1 hunks)
  • packages/standard-server-fastify/src/index.ts (1 hunks)
  • packages/standard-server-fastify/src/request.test.ts (1 hunks)
  • packages/standard-server-fastify/src/request.ts (1 hunks)
  • packages/standard-server-fastify/src/response.test.ts (1 hunks)
  • packages/standard-server-fastify/src/response.ts (1 hunks)
  • packages/standard-server-fastify/tsconfig.json (1 hunks)
💤 Files with no reviewable changes (1)
  • apps/content/docs/openapi/integrations/implement-contract-in-nest.md
🧰 Additional context used
🧬 Code graph analysis (12)
packages/standard-server-fastify/src/request.ts (3)
packages/standard-server/src/types.ts (1)
  • StandardLazyRequest (26-32)
packages/standard-server-node/src/method.ts (1)
  • toStandardMethod (1-3)
packages/shared/src/function.ts (1)
  • once (3-16)
packages/openapi/src/adapters/fastify/openapi-handler.test.ts (2)
packages/server/src/builder.ts (2)
  • handler (273-280)
  • os (336-352)
packages/openapi/src/adapters/fastify/openapi-handler.ts (1)
  • OpenAPIHandler (16-20)
packages/openapi/src/adapters/fastify/openapi-handler.ts (4)
packages/server/src/context.ts (1)
  • Context (1-1)
packages/server/src/adapters/fastify/handler.ts (2)
  • FastifyHandlerOptions (18-20)
  • FastifyHandler (22-62)
packages/openapi/src/adapters/standard/openapi-handler.ts (2)
  • StandardOpenAPIHandlerOptions (10-12)
  • StandardOpenAPIHandler (14-24)
packages/server/src/router.ts (1)
  • Router (12-17)
packages/server/src/adapters/fastify/rpc-handler.ts (5)
packages/server/src/context.ts (1)
  • Context (1-1)
packages/server/src/adapters/fastify/handler.ts (2)
  • FastifyHandlerOptions (18-20)
  • FastifyHandler (22-62)
packages/server/src/adapters/standard/rpc-handler.ts (2)
  • StandardRPCHandlerOptions (11-13)
  • StandardRPCHandler (15-24)
packages/server/src/router.ts (1)
  • Router (12-17)
packages/server/src/plugins/strict-get-method.ts (1)
  • StrictGetMethodPlugin (24-66)
packages/server/src/adapters/fastify/handler.test-d.ts (1)
packages/server/src/adapters/fastify/handler.ts (1)
  • FastifyHandler (22-62)
packages/standard-server-fastify/src/response.test.ts (1)
packages/standard-server-fastify/src/response.ts (1)
  • sendStandardResponse (8-34)
packages/server/src/adapters/fastify/handler.ts (7)
packages/server/src/context.ts (1)
  • Context (1-1)
packages/server/src/adapters/standard/handler.ts (2)
  • StandardHandleOptions (16-19)
  • StandardHandler (50-183)
packages/standard-server-fastify/src/response.ts (2)
  • SendStandardResponseOptions (6-6)
  • sendStandardResponse (8-34)
packages/shared/src/interceptor.ts (1)
  • Interceptor (13-16)
packages/shared/src/array.ts (1)
  • toArray (1-3)
packages/shared/src/args.ts (2)
  • MaybeOptionalOptions (1-3)
  • resolveMaybeOptionalOptions (5-7)
packages/server/src/adapters/standard/utils.ts (2)
  • FriendlyStandardHandleOptions (4-6)
  • resolveFriendlyStandardHandleOptions (8-13)
packages/standard-server-fastify/src/response.ts (2)
packages/standard-server-node/src/body.ts (2)
  • ToNodeHttpBodyOptions (53-53)
  • toNodeHttpBody (60-104)
packages/standard-server/src/types.ts (2)
  • StandardResponse (34-41)
  • StandardHeaders (1-3)
packages/server/src/adapters/fastify/rpc-handler.test.ts (2)
packages/server/src/builder.ts (2)
  • handler (273-280)
  • os (336-352)
packages/server/src/adapters/fastify/rpc-handler.ts (1)
  • RPCHandler (24-33)
packages/nest/src/implement.test.ts (1)
packages/standard-server-fastify/src/index.ts (1)
  • FastifyReply (4-4)
packages/server/src/adapters/fastify/handler.test.ts (2)
packages/server/src/adapters/fastify/handler.ts (2)
  • handle (34-61)
  • FastifyHandler (22-62)
packages/standard-server-fastify/src/response.ts (1)
  • sendStandardResponse (8-34)
packages/standard-server-fastify/src/request.test.ts (2)
packages/standard-server-fastify/src/request.ts (1)
  • toStandardLazyRequest (6-26)
packages/standard-server/src/types.ts (1)
  • StandardLazyRequest (26-32)
🪛 LanguageTool
packages/standard-server-fastify/README.md

[style] ~54-~54: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ... your API or implement API contract. - [@orpc/client](https://www.npmjs.com/package/@...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~55-~55: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ... API on the client with type-safety. - [@orpc/openapi](https://www.npmjs.com/package/...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~56-~56: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...I specs and handle OpenAPI requests. - [@orpc/otel](https://www.npmjs.com/package/@or...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~57-~57: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ....io/) integration for observability. - [@orpc/nest](https://www.npmjs.com/package/@or...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~58-~58: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ... with NestJS. - [@orpc/react](https://www.npmjs.com/package/@o...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~59-~59: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...with React and React Server Actions. - [@orpc/tanstack-query](https://www.npmjs.com/p...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~60-~60: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...stack.com/query/latest) integration. - [@orpc/experimental-react-swr](https://www.npm...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~61-~61: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...ttps://swr.vercel.app/) integration. - [@orpc/vue-colada](https://www.npmjs.com/packa...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~62-~62: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...ada](https://pinia-colada.esm.dev/). - [@orpc/hey-api](https://www.npmjs.com/package/...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~63-~63: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...I](https://heyapi.dev/) integration. - [@orpc/zod](https://www.npmjs.com/package/@orp...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~64-~64: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...tps://zod.dev/) doesn't support yet. - [@orpc/valibot](https://www.npmjs.com/package/...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~65-~65: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...rom Valibot. - [@orpc/arktype](https://www.npmjs.com/package/...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

🪛 markdownlint-cli2 (0.18.1)
packages/standard-server-fastify/README.md

48-48: Link text should be descriptive

(MD059, descriptive-link-text)


75-75: Images should have alternate text (alt text)

(MD045, no-alt-text)

🔇 Additional comments (35)
packages/standard-server-fastify/.gitignore (1)

1-26: ✓ Standard .gitignore configuration looks good.

The file follows common Node.js .gitignore patterns and provides comprehensive coverage for generated artifacts (build outputs, node_modules, logs, TypeScript metadata, test temps, etc.). The explicit exceptions for .gitignore and .*.example files are appropriate.

packages/standard-server-fastify/tsconfig.json (1)

1-19: TypeScript configuration structure is sound.

The configuration properly extends the base library configuration, targets ES2022 for a modern library, includes appropriate excludes for test/mock files, and restricts compilation to the src directory. The setup aligns with standard monorepo practices.

packages/standard-server-fastify/src/request.ts (2)

13-15: Verify the inconsistency between req.raw and direct req property access.

Lines 13-14 access req.raw.method and req.raw for URL conversion, but line 15 uses req.headers directly instead of req.raw.headers. This inconsistency may lead to unexpected behavior if Fastify normalizes or transforms headers differently from the raw Node.js request.

Please verify whether this is intentional. If headers should match the raw request for consistency, apply this diff:

   return {
     method: toStandardMethod(req.raw.method),
     url: toStandardUrl(req.raw),
-    headers: req.headers,
+    headers: req.raw.headers,
     body: once(async () => {

16-23: Consider edge cases in body resolution logic.

The condition req.body !== undefined (line 18) prefers Fastify's parsed body when defined. However, if Fastify explicitly sets req.body = undefined in some edge cases (e.g., after certain content-type handling or errors), this could unintentionally skip the parsed body and fall back to raw parsing.

Please verify that Fastify never explicitly assigns undefined to req.body in scenarios where body parsing was attempted. If this is a concern, consider checking for the presence of the property using 'body' in req or relying on Fastify's content-type handling flags.

packages/server/package.json (1)

58-62: LGTM! Fastify adapter exports and dependencies are properly configured.

The new Fastify adapter export structure follows the established pattern for other adapters (node, fetch, aws-lambda), with appropriate workspace dependency on @orpc/standard-server-fastify and devDependency on fastify for testing.

Also applies to: 104-104, 139-139, 149-149

packages/standard-server-fastify/package.json (1)

36-42: Verify whether fastify should be a required peer dependency.

The fastify peer dependency is marked as optional (lines 40-42), but packages/standard-server-fastify/src/request.ts directly imports FastifyRequest and FastifyReply types from the fastify package. This creates a mismatch: TypeScript compilation will fail if fastify is not installed, making the "optional" designation potentially misleading.

Please verify whether consumers can realistically use this package without fastify installed. If not, consider removing the optional: true flag to make it a required peer dependency:

   "peerDependenciesMeta": {
     "fastify": {
-      "optional": true
     }
   },
packages/nest/package.json (1)

60-60: LGTM! Nest package dependencies updated for Fastify integration.

The additions of @orpc/standard-server-fastify and @fastify/cookie align with the PR's objective to provide native Fastify support for NestJS integration. The dependency structure is appropriate given that fastify is already an optional peer dependency.

Also applies to: 64-64

apps/content/learn-and-contribute/overview.md (1)

40-40: LGTM! Documentation updated to include the new Fastify adapter.

The addition of the standard-server-fastify link follows the established documentation pattern and is correctly placed within the Standard Server section.

packages/openapi/src/adapters/fastify/index.ts (1)

1-1: LGTM!

The re-export pattern follows standard module consolidation practices and provides a clean public API surface for the Fastify OpenAPI adapter.

packages/server/src/adapters/fastify/handler.test-d.ts (1)

1-17: LGTM!

The TypeScript declaration tests properly validate the context type behavior, ensuring that optional contexts don't require the context parameter while required contexts do. The use of @ts-expect-error on Line 14 correctly documents the expected type error.

packages/server/src/adapters/fastify/index.ts (1)

1-2: LGTM!

The re-exports consolidate the Fastify adapter's public API surface, following the standard pattern used across the codebase.

packages/standard-server-fastify/src/index.ts (1)

1-4: LGTM!

The exports properly consolidate the package's public API. The use of type-only exports on Line 4 for Fastify types is correct and prevents unnecessary runtime imports.

apps/content/docs/adapters/http.md (2)

139-147: LGTM! Clear integration pattern.

The route handler and 404 fallback follow the standard pattern used across other adapters. The prefix handling and matched flag check are consistent with the existing examples.


133-137: This pattern is intentional and correctly recommended for full oRPC feature support.

The content-type parser pattern is deliberately designed to allow oRPC to handle all body parsing directly, enabling advanced features like Bracket Notation and proper File handling that wouldn't work with framework-level parsing conflicts. This is validated by the test demonstrating the trade-off explicitly.

The original review's concern about differing from other adapters needs context: Express and Next.js use middleware architectures and recommend different integration patterns for that reason, not because the Fastify approach is wrong. Documentation already acknowledges this in fastify.md (warning about default content-type parsing).

Suggested improvements (not critical issues):

  • Add similar context to the HTTP.md example explaining why the parser is configured this way and how oRPC handles body validation internally
  • Reference the "Extend Body Parser" guide for handling large payloads if needed
packages/nest/src/implement.test.ts (2)

468-503: Good coverage of Fastify cookie integration.

The test validates that:

  • Fastify cookie plugin integrates with oRPC handlers
  • The @res decorator with passthrough mode works correctly
  • Response headers include both oRPC-set headers and Fastify-set cookies

This effectively demonstrates the native Fastify integration benefits mentioned in the PR objectives.


483-483: The as any cast addresses a documented version incompatibility, not a code defect.

The @nestjs/platform-fastify package depends on Fastify version 4.28.1, while newer versions of @fastify/cookie require Fastify ^5.0.0. This causes a TypeScript error: "Argument of type 'FastifyCookie' is not assignable to parameter of type 'FastifyPluginCallback...'" The cast is a known workaround; upgrading all NestJS packages resolves the underlying type incompatibility. If dependencies are already aligned to compatible versions in this codebase, the cast can be removed. Otherwise, it remains necessary and should be retained until the version mismatch is resolved.

packages/server/src/adapters/fastify/rpc-handler.test.ts (2)

7-10: LGTM!

The handler setup demonstrates both explicit route configuration (ping with GET method) and implicit handler configuration (pong without method specification), which is useful for testing different handler patterns.


26-37: Good validation of default plugin behavior.

The test correctly validates that StrictGetMethodPlugin is enabled by default, ensuring GET requests to procedures without explicit method configuration are rejected with 405. This enforces proper HTTP method semantics.

packages/openapi/package.json (1)

43-47: Export configuration verified; confirm dist files after build.

The fastify export is correctly configured:

  • Export points to existing source file: ./src/adapters/fastify/index.ts
  • publishConfig exports properly mapped to dist paths ✅
  • Source and test files present ✅

Dist files (./dist/adapters/fastify/index.mjs and ./dist/adapters/fastify/index.d.mts) can only be verified after running pnpm -w build. Ensure the build completes successfully and produces these output files.

packages/standard-server-fastify/src/response.ts (3)

1-6: LGTM!

The imports and interface definition are well-structured. The empty extension of ToNodeHttpBodyOptions creates a semantic alias that allows future extensibility without breaking changes.


13-15: LGTM!

The Promise wrapper with reply.raw event handlers is the correct pattern for handling Fastify response lifecycle events, ensuring proper resolution on successful completion and rejection on transmission errors.


17-22: LGTM!

The header cloning and body conversion logic is correct. The shallow clone protects the original standardResponse.headers from mutation, while toNodeHttpBody appropriately mutates the cloned headers to set the correct content-type and content-disposition based on body type.

apps/content/docs/adapters/fastify.md (1)

37-46: LGTM!

The route handler example correctly demonstrates the integration pattern, including prefix stripping, context provision, and 404 handling for unmatched routes.

packages/server/src/adapters/fastify/rpc-handler.ts (1)

1-16: LGTM!

The interface definition correctly composes options from both the Fastify adapter layer and the standard RPC handler layer, with clear documentation for the plugin enablement flag.

packages/server/src/adapters/fastify/handler.test.ts (3)

1-18: LGTM!

The mock setup is well-structured with proper isolation and cleanup. The use of vi.clearAllMocks() in beforeEach ensures test independence.


20-52: LGTM!

The test setup cleverly uses an actual Fastify instance to capture real FastifyRequest and FastifyReply objects, which is more robust than mocking these complex Fastify types. The pattern of using fastify.ready() followed by a test request to populate fixtures is appropriate for this use case.


54-99: LGTM!

The 'on match' test case provides comprehensive coverage, validating all interactions including the standard handler call, request conversion, response sending, and interceptor execution with correct context propagation.

packages/standard-server-fastify/src/response.test.ts (6)

1-13: LGTM!

The test setup correctly uses spies on the actual implementation rather than full mocks, allowing validation of both the function calls and the actual behavior. The beforeEach cleanup ensures test isolation.


15-51: LGTM!

The test thoroughly validates the empty body scenario, including proper cleanup, correct function calls, and expected HTTP response characteristics.


53-136: LGTM!

Both the JSON body and file streaming tests comprehensively validate their respective scenarios, including correct content-type headers, body serialization, and streaming behavior.


138-184: LGTM!

The async generator streaming test thoroughly validates server-sent events (SSE) formatting, including correct event types, data serialization, and content-type headers.


186-251: LGTM!

The edge case tests provide excellent coverage for error conditions and resource cleanup, including stream error propagation and proper cancellation handling when requests are aborted.


253-280: LGTM!

The cookie integration test is valuable for validating compatibility with Fastify's plugin ecosystem, ensuring that sendStandardResponse doesn't interfere with other Fastify plugins like @fastify/cookie.

packages/server/src/adapters/fastify/handler.ts (2)

1-20: LGTM!

The type definitions are well-structured and correctly model the Fastify adapter layer's concerns. The FastifyHandleResult appropriately simplifies the response model since responses are sent directly through Fastify rather than returned.


34-61: LGTM!

The handle method implementation correctly orchestrates the request flow, properly converting between Fastify and standard representations, delegating to the standard handler, and handling both matched and unmatched cases appropriately.

Copy link
Copy Markdown

@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)
packages/server/src/adapters/fastify/handler.ts (1)

22-22: Note: The self-referential implements clause has been correctly removed.

A previous review comment flagged this issue and it appears to have been addressed. The class declaration is now correct.

🧹 Nitpick comments (1)
packages/standard-server-fastify/src/response.ts (1)

13-24: Consider wrapping response operations in try-catch.

If toNodeHttpBody, reply.status(), reply.headers(), or reply.send() throw synchronously, the promise will reject but the event listeners may not be properly cleaned up, depending on timing.

Wrap the operations in a try-catch to ensure proper error handling:

     reply.raw.once('error', onError)
     reply.raw.once('close', onClose)

+    try {
       const resHeaders: StandardHeaders = { ...standardResponse.headers }

       const resBody = toNodeHttpBody(standardResponse.body, resHeaders, options)

       reply.status(standardResponse.status)
       reply.headers(resHeaders)
       reply.send(resBody)
+    } catch (err) {
+      reply.raw.off('error', onError)
+      reply.raw.off('close', onClose)
+      reject(err)
+    }
   })
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 20bd292 and 3c539a7.

📒 Files selected for processing (7)
  • packages/server/src/adapters/fastify/handler.test.ts (1 hunks)
  • packages/server/src/adapters/fastify/handler.ts (1 hunks)
  • packages/server/src/adapters/node/handler.test.ts (1 hunks)
  • packages/server/src/adapters/node/handler.ts (1 hunks)
  • packages/standard-server-fastify/src/response.test.ts (1 hunks)
  • packages/standard-server-fastify/src/response.ts (1 hunks)
  • packages/standard-server-node/src/response.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/standard-server-fastify/src/response.test.ts
  • packages/server/src/adapters/fastify/handler.test.ts
🧰 Additional context used
🧬 Code graph analysis (4)
packages/server/src/adapters/node/handler.ts (1)
packages/server/src/context.ts (1)
  • Context (1-1)
packages/server/src/adapters/node/handler.test.ts (2)
packages/server/src/adapters/node/handler.ts (1)
  • handle (42-69)
packages/server/src/adapters/standard/handler.ts (1)
  • handle (72-182)
packages/standard-server-fastify/src/response.ts (2)
packages/standard-server-node/src/body.ts (2)
  • ToNodeHttpBodyOptions (53-53)
  • toNodeHttpBody (60-104)
packages/standard-server/src/types.ts (2)
  • StandardResponse (34-41)
  • StandardHeaders (1-3)
packages/server/src/adapters/fastify/handler.ts (7)
packages/server/src/context.ts (1)
  • Context (1-1)
packages/server/src/adapters/standard/handler.ts (2)
  • StandardHandleOptions (16-19)
  • StandardHandler (50-183)
packages/standard-server-fastify/src/response.ts (2)
  • SendStandardResponseOptions (6-6)
  • sendStandardResponse (8-25)
packages/shared/src/interceptor.ts (1)
  • Interceptor (13-16)
packages/shared/src/array.ts (1)
  • toArray (1-3)
packages/shared/src/args.ts (2)
  • MaybeOptionalOptions (1-3)
  • resolveMaybeOptionalOptions (5-7)
packages/server/src/adapters/standard/utils.ts (2)
  • FriendlyStandardHandleOptions (4-6)
  • resolveFriendlyStandardHandleOptions (8-13)
⏰ 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). (3)
  • GitHub Check: publish-commit
  • GitHub Check: lint
  • GitHub Check: test
🔇 Additional comments (4)
packages/standard-server-node/src/response.test.ts (1)

41-42: LGTM! Test precision improved.

The explicit assertions correctly verify that content-type is not set when the body is undefined, while custom headers are still propagated. This is more precise than a permissive toMatchObject match and aligns with the stricter header handling introduced across the Fastify and Node adapters in this PR.

packages/server/src/adapters/node/handler.test.ts (1)

97-128: LGTM! Test correctly aligns with the return type.

The test now correctly expects { matched: false } without the response field, which matches the NodeHttpHandleResult type definition. The Node handler transforms the internal StandardHandleResult (which includes response: undefined) to its own simpler result type.

packages/server/src/adapters/node/handler.ts (1)

26-26: LGTM! Removed meaningless self-referential implements clause.

The class no longer implements itself, which was semantically incorrect. This is a good cleanup.

packages/server/src/adapters/fastify/handler.ts (1)

34-61: LGTM! Handler implementation follows the established adapter pattern.

The handle method correctly:

  • Applies adapter interceptors
  • Converts Fastify request/reply to standard request
  • Delegates to the standard handler
  • Sends the response on match
  • Returns the appropriate result type

The implementation is consistent with the Node handler and integrates properly with the standard server infrastructure.

@dinwwwh dinwwwh removed the lgtm This PR has been approved by a maintainer label Oct 24, 2025
@kingdavidmartins
Copy link
Copy Markdown

@unnoq I spent the last day using this release and it worked wonders. Thank you soo much. Can't wait to have this in main.

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

Labels

size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fastify integration via plugin rather than sideloaded server

2 participants