Skip to content

Conversation

@schiller-manuel
Copy link
Contributor

@schiller-manuel schiller-manuel commented Sep 28, 2025

fixes #5271

fixes #5255

Summary by CodeRabbit

  • New Features

    • Server functions can return Response objects (sync or async); such Responses are marked with a new raw-response header so clients can receive them unchanged.
    • Added a public constant to indicate raw responses.
  • Refactor

    • Simplified exported fetcher/server-fn TypeScript generics and unified return typing.
    • Explicitly typed start options and streamlined internal optional chaining for safer startup/serialization access.
  • Tests

    • Expanded type tests for async/sync handlers returning Response, arrays, and unions.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 28, 2025

Walkthrough

Adds a raw-response pathway: introduces and exports X_TSS_RAW_RESPONSE, server sets this header when returning a Response, client fetcher short-circuits and returns the raw Response immediately, and server-function typings/tests updated to accept Response and various return shapes.

Changes

Cohort / File(s) Summary of Changes
Client constants & exports
packages/start-client-core/src/constants.ts, packages/start-client-core/src/index.tsx
Added and exported X_TSS_RAW_RESPONSE = 'x-tss-raw'.
Client fetcher control flow
packages/start-client-core/src/client-rpc/serverFnFetcher.ts
Imported X_TSS_RAW_RESPONSE and added early-return: if response header x-tss-raw === 'true', return the raw Response immediately, bypassing content-type processing and serialization.
Fetcher & server-fn typing changes
packages/start-client-core/src/createServerFn.ts, packages/start-client-core/src/tests/createServerFn.test-d.ts, packages/react-start-client/src/tests/createServerFn.test-d.tsx
Removed TRegister from public Fetcher generics; introduced new FetcherData<TResponse> mapping and adjusted ServerFnReturnType/handler signatures; added/refined type tests for Response, arrays, and unions (sync/async).
Server handler integration
packages/start-server-core/src/server-functions-handler.ts
Imported X_TSS_RAW_RESPONSE and sets header x-tss-raw: "true" on Response results so clients can detect raw responses.
Start options & seroval minor tweaks
packages/start-client-core/src/getStartOptions.ts, packages/start-client-core/src/getDefaultSerovalPlugins.ts
Added explicit return type for getStartOptions; used optional chaining for reading start.serializationAdapters and adjusted related types.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor U as User Code
  participant C as Client API
  participant F as serverFnFetcher
  participant S as Server handler

  U->>C: call serverFn(...)
  C->>F: invoke fetcher
  F->>S: HTTP request
  alt Server returns raw Response
    S-->>F: Response (headers include x-tss-raw: "true")
    Note right of F #DDEBF7: detect header and short-circuit\nreturn raw Response
    F-->>C: raw Response
    C-->>U: Response
  else Server returns serialized payload
    S-->>F: serialized body (JSON)
    F->>F: parse & deserialize
    F-->>C: structured data
    C-->>U: data
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • chorobin

Poem

I thump my paw—response anew,
A tiny header lets raw shine through.
No more undefined surprise,
Async Responses now fly by.
Hop, ship, done—carrots for you! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning Beyond the required raw Response logic and typing fixes, the pull request also refactors the Fetcher and createServerFn API generics, adds optional chaining changes in serialization plugins and getStartOptions, and includes type-coverage tests for unrelated scenarios, which are not mandated by issues #5271 and #5255. Consider isolating the raw Response header/short-circuit and typing adjustments into this PR and moving the broader Fetcher API refactors, serialization and options modifications, and extra tests into separate follow-up pull requests to keep the scope focused on the linked issues.
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.
✅ 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 succinctly captures the primary change to the pull request, which is fixing how raw Responses are handled in both the client and server code, and it avoids generic or irrelevant wording, making it clear to reviewers what the PR addresses.
Linked Issues Check ✅ Passed The changes introduce a new header and client-side short-circuit to correctly return raw Responses (resolving undefined returns and Bun errors), and the updated type definitions allow Response to flow through without serialization errors, fully satisfying the runtime and typing requirements of both issues #5271 and #5255.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-async-response-type

📜 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 39f3e54 and 1265836.

📒 Files selected for processing (3)
  • packages/start-client-core/src/createServerFn.ts (5 hunks)
  • packages/start-client-core/src/getDefaultSerovalPlugins.ts (1 hunks)
  • packages/start-client-core/src/getStartOptions.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • packages/start-client-core/src/getStartOptions.ts
  • packages/start-client-core/src/getDefaultSerovalPlugins.ts
  • packages/start-client-core/src/createServerFn.ts
packages/{*-start,start-*}/**

📄 CodeRabbit inference engine (AGENTS.md)

Name and place Start framework packages under packages/-start/ or packages/start-/

Files:

  • packages/start-client-core/src/getStartOptions.ts
  • packages/start-client-core/src/getDefaultSerovalPlugins.ts
  • packages/start-client-core/src/createServerFn.ts
🧬 Code graph analysis (1)
packages/start-client-core/src/createServerFn.ts (3)
packages/start-client-core/src/getStartOptions.ts (1)
  • getStartOptions (5-8)
packages/start-client-core/src/createMiddleware.ts (1)
  • IntersectAllValidatorInputs (177-185)
packages/router-core/src/ssr/serializer/transformer.ts (1)
  • ValidateSerializableInput (202-205)
⏰ 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: Preview
  • GitHub Check: Test
🔇 Additional comments (7)
packages/start-client-core/src/getDefaultSerovalPlugins.ts (1)

8-17: LGTM! Defensive handling of potentially undefined start options.

The optional chaining correctly handles cases where getStartOptions() returns undefined, preventing runtime errors when accessing serializationAdapters.

packages/start-client-core/src/getStartOptions.ts (1)

5-8: LGTM! Explicit typing improves type safety.

The explicit return type () => AnyStartInstanceOptions | undefined clearly signals that start options may be unavailable, aligning with the defensive changes throughout the client core.

packages/start-client-core/src/createServerFn.ts (5)

171-176: LGTM! Safe handling of potentially undefined start options.

The optional chaining at line 176 correctly handles cases where getStartOptions() returns undefined, consistent with the defensive changes across the client core.


245-274: LGTM! Simplified fetcher types support raw Response returns.

The removal of TRegister from the Fetcher type signature and the updated return types using FetcherData<TResponse> correctly prepare the public API to handle raw Response objects, addressing the TypeScript errors mentioned in issue #5271.


297-302: LGTM! Conditional type correctly handles raw Response returns.

The new FetcherData<TResponse> conditional type properly distinguishes between:

  1. Raw Response objects (returned as-is)
  2. JsonResponse<T> wrappers (JSON type extracted)
  3. Other types (standard awaited handling)

This enables the raw-response pathway described in the PR objectives and fixes the TypeScript errors from issue #5271.


304-307: LGTM! Response exemption from serialization validation.

The conditional in ServerFnReturnType correctly exempts Response objects from serialization validation while preserving validation for other types. This directly fixes the "Function is not serializable" TypeScript error reported in issue #5271.


502-517: LGTM! Handler signature aligns with updated Fetcher types.

The updated handler return type correctly uses the simplified Fetcher<TMiddlewares, TInputValidator, TNewResponse> signature, consistent with the removal of TRegister from the public API and supporting the raw Response handling.


Comment @coderabbitai help to get the list of available commands and usage tips.

@nx-cloud
Copy link

nx-cloud bot commented Sep 28, 2025

View your CI Pipeline Execution ↗ for commit 1265836

Command Status Duration Result
nx affected --targets=test:eslint,test:unit,tes... ✅ Succeeded 4m 45s View ↗
nx run-many --target=build --exclude=examples/*... ✅ Succeeded 27s View ↗

☁️ Nx Cloud last updated this comment at 2025-09-30 21:59:18 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Sep 28, 2025

More templates

@tanstack/arktype-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/arktype-adapter@5276

@tanstack/directive-functions-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/directive-functions-plugin@5276

@tanstack/eslint-plugin-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/eslint-plugin-router@5276

@tanstack/history

npm i https://pkg.pr.new/TanStack/router/@tanstack/history@5276

@tanstack/nitro-v2-vite-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/nitro-v2-vite-plugin@5276

@tanstack/react-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router@5276

@tanstack/react-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-devtools@5276

@tanstack/react-router-ssr-query

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-ssr-query@5276

@tanstack/react-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start@5276

@tanstack/react-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-client@5276

@tanstack/react-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-server@5276

@tanstack/router-cli

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-cli@5276

@tanstack/router-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-core@5276

@tanstack/router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools@5276

@tanstack/router-devtools-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools-core@5276

@tanstack/router-generator

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-generator@5276

@tanstack/router-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-plugin@5276

@tanstack/router-ssr-query-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-ssr-query-core@5276

@tanstack/router-utils

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-utils@5276

@tanstack/router-vite-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-vite-plugin@5276

@tanstack/server-functions-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/server-functions-plugin@5276

@tanstack/solid-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router@5276

@tanstack/solid-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router-devtools@5276

@tanstack/solid-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start@5276

@tanstack/solid-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-client@5276

@tanstack/solid-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-server@5276

@tanstack/start-client-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-client-core@5276

@tanstack/start-plugin-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-plugin-core@5276

@tanstack/start-server-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-core@5276

@tanstack/start-static-server-functions

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-static-server-functions@5276

@tanstack/start-storage-context

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-storage-context@5276

@tanstack/valibot-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/valibot-adapter@5276

@tanstack/virtual-file-routes

npm i https://pkg.pr.new/TanStack/router/@tanstack/virtual-file-routes@5276

@tanstack/zod-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/zod-adapter@5276

commit: 1265836

Copy link
Contributor

@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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 86b1dd9 and 13b3ecf.

📒 Files selected for processing (6)
  • packages/start-client-core/src/client-rpc/serverFnFetcher.ts (2 hunks)
  • packages/start-client-core/src/constants.ts (1 hunks)
  • packages/start-client-core/src/createServerFn.ts (1 hunks)
  • packages/start-client-core/src/index.tsx (1 hunks)
  • packages/start-client-core/src/tests/createServerFn.test-d.ts (2 hunks)
  • packages/start-server-core/src/server-functions-handler.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • packages/start-client-core/src/createServerFn.ts
  • packages/start-client-core/src/constants.ts
  • packages/start-client-core/src/client-rpc/serverFnFetcher.ts
  • packages/start-client-core/src/tests/createServerFn.test-d.ts
  • packages/start-server-core/src/server-functions-handler.ts
  • packages/start-client-core/src/index.tsx
packages/{*-start,start-*}/**

📄 CodeRabbit inference engine (AGENTS.md)

Name and place Start framework packages under packages/-start/ or packages/start-/

Files:

  • packages/start-client-core/src/createServerFn.ts
  • packages/start-client-core/src/constants.ts
  • packages/start-client-core/src/client-rpc/serverFnFetcher.ts
  • packages/start-client-core/src/tests/createServerFn.test-d.ts
  • packages/start-server-core/src/server-functions-handler.ts
  • packages/start-client-core/src/index.tsx
🧬 Code graph analysis (4)
packages/start-client-core/src/constants.ts (1)
packages/start-client-core/src/index.tsx (1)
  • X_TSS_RAW_RESPONSE (88-88)
packages/start-client-core/src/client-rpc/serverFnFetcher.ts (2)
packages/start-client-core/src/constants.ts (1)
  • X_TSS_RAW_RESPONSE (8-8)
packages/start-client-core/src/index.tsx (1)
  • X_TSS_RAW_RESPONSE (88-88)
packages/start-client-core/src/tests/createServerFn.test-d.ts (1)
packages/start-client-core/src/createServerFn.ts (1)
  • createServerFn (51-170)
packages/start-server-core/src/server-functions-handler.ts (2)
packages/start-client-core/src/constants.ts (1)
  • X_TSS_RAW_RESPONSE (8-8)
packages/start-client-core/src/index.tsx (1)
  • X_TSS_RAW_RESPONSE (88-88)
⏰ 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: Test
  • GitHub Check: Preview

Comment on lines +149 to 150
result.result.headers.set(X_TSS_RAW_RESPONSE, 'true')
return result.result
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Setting a header on an immutable Response will throw at runtime

When developers return a Response obtained from fetch (very common for proxying upstream resources), response.headers is immutable. Calling set on it throws TypeError: Headers guard is immutable, so this path will crash before the response ever reaches the client—precisely the scenario this PR is meant to fix. Instead of mutating the existing headers, clone them (and the response metadata) into a new Response that includes X_TSS_RAW_RESPONSE.

Apply this diff to fix the issue:

-      if (result.result instanceof Response) {
-        result.result.headers.set(X_TSS_RAW_RESPONSE, 'true')
-        return result.result
-      }
+      if (result.result instanceof Response) {
+        const rawResponse = result.result
+        const headers = new Headers(rawResponse.headers)
+        headers.set(X_TSS_RAW_RESPONSE, 'true')
+        return new Response(rawResponse.body, {
+          status: rawResponse.status,
+          statusText: rawResponse.statusText,
+          headers,
+        })
+      }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
result.result.headers.set(X_TSS_RAW_RESPONSE, 'true')
return result.result
if (result.result instanceof Response) {
const rawResponse = result.result
const headers = new Headers(rawResponse.headers)
headers.set(X_TSS_RAW_RESPONSE, 'true')
return new Response(rawResponse.body, {
status: rawResponse.status,
statusText: rawResponse.statusText,
headers,
})
}
🤖 Prompt for AI Agents
In packages/start-server-core/src/server-functions-handler.ts around lines
149-150, the code attempts to call result.result.headers.set(...) on a Response
returned from fetch, but fetch Responses have immutable headers and calling set
throws at runtime; instead, create a new Response cloning the original body and
metadata and use a mutable copy of the headers: construct a new
Headers(result.result.headers), call set('X-TSS-RAW_RESPONSE','true') on that
copy, then return new Response(result.result.body, { status:
result.result.status, statusText: result.result.statusText, headers:
headersCopy, /* copy any other relevant properties like duplex if used */ });
this avoids mutating the original immutable Headers and preserves the original
response metadata and body.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Server function returns undefined on raw response value Raw Response from server function not working on Bun runtime

2 participants