Skip to content

Conversation

@schiller-manuel
Copy link
Contributor

@schiller-manuel schiller-manuel commented Dec 25, 2025

Summary by CodeRabbit

  • Refactor
    • Streamlined server function request/response flow with clearer GET/POST handling and payload encoding.
    • Introduced module-level caching for serialization plugins to reduce per-request work.
    • Unified payload serialization/parsing paths, disallowed FormData for GET, and simplified header handling.
    • Cleaned up error/response processing for more consistent behavior.

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

@schiller-manuel schiller-manuel changed the title only handle createServerFn refactor: only handle createServerFn Dec 25, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 25, 2025

📝 Walkthrough

Walkthrough

Streamlines client and server server-function request/response flows: client simplifies argument/header handling and unifies GET/POST serialization; server adds cached Seroval plugins and FormData constants, simplifies payload extraction for GET/POST, and removes createServerFn-specific branches. (48 words)

Changes

Cohort / File(s) Summary
Client-side request handling
packages/start-client-core/src/client-rpc/serverFnFetcher.ts
Treats first arg as FunctionMiddlewareClientFnOptions; uses hasOwnProperties to detect context; consolidates header creation (always sets x-tsr-redirect: manual); moves GET payload into serialized query string (rejects FormData for GET); computes POST fetch body via getFetchBody and sets content-type; consolidates response handling with try/catch that treats some errors as Responses.
Server-side request processing
packages/start-server-core/src/server-functions-handler.ts
Adds module-level serovalPlugins cache and FORM_DATA_CONTENT_TYPES; normalizes method checks via methodLower; reads GET payload from URL searchParams; parses POST JSON then deserializes with cached plugins; removes createServerFn-specific paths and streaming/RSC-related code; merges/normalizes context and payload before invoking server function.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client
  participant FetchLayer as Fetch
  participant ServerHandler as Server
  participant Seroval as SerovalPlugins

  rect rgb(235,248,255)
    Note over Client,FetchLayer: Client builds request
    Client->>Client: determine options (headers/context)\nserialize payload (GET->query / POST->body)
    Client->>FetchLayer: send HTTP request (x-tsr-redirect: manual)
  end

  rect rgb(250,245,235)
    Note over FetchLayer,ServerHandler: Server receives request
    FetchLayer->>ServerHandler: HTTP request (method, headers, payload)
    ServerHandler->>ServerHandler: methodLower check\nextract payload (GET from searchParams / POST parse JSON)
    ServerHandler->>Seroval: deserialize payload via cached plugins
    Seroval-->>ServerHandler: deserialized payload/context
    ServerHandler->>ServerHandler: invoke server function with normalized payload
    ServerHandler-->>FetchLayer: HTTP response (serialized result or error)
  end

  rect rgb(235,255,240)
    Note over FetchLayer,Client: Client processes response
    FetchLayer-->>Client: response
    Client->>Client: getResponse try/catch\nhandle Response or throw
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • chorobin

Poem

🐰 I nibble bytes and tidy routes,
Headers snug, no messy roots,
Cache the plugins, skip the maze,
GET in query, POST in praise,
Hop—requests now find their routes!

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title 'refactor: only handle createServerFn' is vague and contradictory. The summary shows the changes actually REMOVE createServerFn handling logic, not 'only handle' it. The title doesn't accurately reflect the main refactoring. Revise title to accurately describe the changes, such as 'refactor: remove createServerFn logic and simplify GET/POST handling' or similar.
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ 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 only-createServerFn

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

@nx-cloud
Copy link

nx-cloud bot commented Dec 25, 2025

View your CI Pipeline Execution ↗ for commit bd33dee

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

☁️ Nx Cloud last updated this comment at 2025-12-25 22:56:13 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Dec 25, 2025

More templates

@tanstack/arktype-adapter

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

@tanstack/eslint-plugin-router

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

@tanstack/history

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

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

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

@tanstack/react-start-client

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

@tanstack/react-start-server

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

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

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

@tanstack/solid-start-client

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

@tanstack/solid-start-server

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

@tanstack/start-client-core

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

@tanstack/start-fn-stubs

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-fn-stubs@6226

@tanstack/start-plugin-core

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

@tanstack/start-server-core

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

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

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

@tanstack/valibot-adapter

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

@tanstack/virtual-file-routes

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

@tanstack/vue-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-router@6226

@tanstack/vue-router-devtools

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

@tanstack/vue-router-ssr-query

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

@tanstack/vue-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-start@6226

@tanstack/vue-start-client

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

@tanstack/vue-start-server

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

@tanstack/zod-adapter

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

commit: bd33dee

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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/start-server-core/src/server-functions-handler.ts (1)

30-30: Improve type safety to align with TypeScript strict mode guidelines.

Multiple uses of the any type throughout this file violate the coding guideline to "Use TypeScript strict mode with extensive type safety." While some dynamic typing may be necessary for server functions, consider:

  1. Adding a generic type parameter to parsePayload to preserve type information
  2. Using unknown instead of any where the type is truly dynamic and forcing type guards
  3. Defining specific interfaces for context and payload shapes

As per coding guidelines, TypeScript strict mode should be used with extensive type safety.

🔎 Example improvements for type safety
// Define payload interface
interface ServerFnPayload {
  context?: Record<string, unknown>
  data?: unknown
}

// Add type parameter to parsePayload
function parsePayload<T = unknown>(payload: unknown): T {
  const parsedPayload = fromJSON(payload, { plugins: serovalPlugins })
  return parsedPayload as T
}

// Use more specific context type
interface ServerFnContext {
  [key: string]: unknown
}

// Update function signature
export const handleServerAction = async ({
  request,
  context,
}: {
  request: Request
  context: ServerFnContext
}) => {
  // ...
}

Also applies to: 64-67, 114-114, 131-131, 148-148

🧹 Nitpick comments (1)
packages/start-client-core/src/client-rpc/serverFnFetcher.ts (1)

19-27: Consider a more descriptive loop variable name.

The variable _ is conventionally used for unused variables, but here it's used in the hasOwnProperty check. A more descriptive name like key or prop would improve readability.

🔎 Proposed refactor
 const hop = Object.prototype.hasOwnProperty
 function hasOwnProperties(obj: object): boolean {
-  for (const _ in obj) {
-    if (hop.call(obj, _)) {
+  for (const key in obj) {
+    if (hop.call(obj, key)) {
       return true
     }
   }
   return false
 }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5463487 and fc114d8.

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

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript strict mode with extensive type safety for all code

Files:

  • packages/start-server-core/src/server-functions-handler.ts
  • packages/start-client-core/src/client-rpc/serverFnFetcher.ts
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Implement ESLint rules for router best practices using the ESLint plugin router

Files:

  • packages/start-server-core/src/server-functions-handler.ts
  • packages/start-client-core/src/client-rpc/serverFnFetcher.ts
🧠 Learnings (2)
📚 Learning: 2025-12-24T22:47:44.320Z
Learnt from: schiller-manuel
Repo: TanStack/router PR: 6211
File: e2e/react-start/i18n-paraglide/src/server.ts:6-6
Timestamp: 2025-12-24T22:47:44.320Z
Learning: In TanStack Router projects using `inlang/paraglide-js`, the callback passed to `paraglideMiddleware` should use `() => handler.fetch(req)` (referencing the outer `req`) instead of `({ request }) => handler.fetch(request)`. This is intentional because the router needs the untouched URL to perform its own rewrite logic with `deLocalizeUrl`/`localizeUrl`. The middleware's processed request would delocalize the URL and interfere with the router's rewrite handling.

Applied to files:

  • packages/start-client-core/src/client-rpc/serverFnFetcher.ts
📚 Learning: 2025-08-30T09:12:13.852Z
Learnt from: Sheraff
Repo: TanStack/router PR: 5051
File: packages/router-core/src/utils.ts:310-315
Timestamp: 2025-08-30T09:12:13.852Z
Learning: In TanStack Router's deepEqual utility, using for..in instead of Object.keys() in getObjectKeys() when ignoreUndefined=true is acceptable because it's called only after isPlainObject() checks, which ensure objects have standard Object prototype chains with no inherited enumerable properties.

Applied to files:

  • packages/start-client-core/src/client-rpc/serverFnFetcher.ts
🧬 Code graph analysis (2)
packages/start-server-core/src/server-functions-handler.ts (2)
packages/start-client-core/src/index.tsx (1)
  • getDefaultSerovalPlugins (101-101)
packages/start-client-core/src/getDefaultSerovalPlugins.ts (1)
  • getDefaultSerovalPlugins (8-17)
packages/start-client-core/src/client-rpc/serverFnFetcher.ts (2)
packages/start-client-core/src/index.tsx (1)
  • FunctionMiddlewareClientFnOptions (29-29)
packages/start-server-core/src/request-response.ts (1)
  • getResponse (399-402)
⏰ 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 (6)
packages/start-server-core/src/server-functions-handler.ts (2)

42-42: LGTM! Consistent use of cached values and variables.

The lazy initialization of serovalPlugins, the use of methodLower for consistency, and the reference to FORM_DATA_CONTENT_TYPES are all clean improvements.

Also applies to: 57-60, 74-76, 80-80


122-133: The client-side code in serverFnFetcher.ts explicitly ensures Content-Type: application/json is set for all JSON payloads (line 132), and FormData requests are handled correctly with the browser-set multipart/form-data header. POST requests with no data naturally result in an empty payload {}, which is the intended behavior. This implementation is working as designed.

Likely an incorrect or invalid review comment.

packages/start-client-core/src/client-rpc/serverFnFetcher.ts (4)

52-68: Excellent validation for GET requests with FormData.

The explicit check to reject FormData for GET requests is good defensive programming. FormData cannot be serialized into URL query parameters, and the clear error message helps developers understand the constraint.

The unified handling of GET payload serialization into the query string is also clean and well-structured.


100-100: Good optimization with hasOwnProperties.

Replacing Object.keys(opts.context).length > 0 with hasOwnProperties(opts.context) is more efficient since it short-circuits on the first property found. The proper truthy check opts.context && before calling the helper ensures safe handling.

Also applies to: 122-122


146-156: Clean simplification of error handling.

The refactored try/catch structure is much clearer than the previous implementation. Treating Response instances thrown as errors as valid responses is a common pattern in fetch-based APIs, and the explicit instanceof check makes the intent obvious.


37-42: The removal of the isPlainObject check is safe. All calls to serverFnFetcher originate exclusively from createClientRpc, which is only invoked through createServerFn().handler(). The Babel plugin ensures proper argument structure at compile time, and the middleware chain in createServerFn constructs args[0] with the expected FunctionMiddlewareClientFnOptions shape. The narrowing is justified and requires no changes.

Comment on lines +12 to +23
import type { Plugin as SerovalPlugin } from 'seroval'

let regex: RegExp | undefined = undefined

// Cache serovalPlugins at module level to avoid repeated calls
let serovalPlugins: Array<SerovalPlugin<any, any>> | undefined = undefined

// Known FormData 'Content-Type' header values - module-level constant
const FORM_DATA_CONTENT_TYPES = [
'multipart/form-data',
'application/x-www-form-urlencoded',
]
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 | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the implementation of getDefaultSerovalPlugins
rg -n 'getDefaultSerovalPlugins' -t ts -t tsx -A 15

# Also check where seroval is imported from
rg -n 'from.*seroval|import.*seroval' -t ts -t tsx

# Check the file structure and surrounding code
cat -n packages/start-server-core/src/server-functions-handler.ts | head -50

Repository: TanStack/router

Length of output: 2064


🏁 Script executed:

#!/bin/bash
# Find where serovalPlugins is initialized/assigned
rg -n 'serovalPlugins\s*=' packages/start-server-core/src/server-functions-handler.ts -A 3 -B 1

# Look for more context in the file, particularly around lines 57-60 mentioned in review
sed -n '50,75p' packages/start-server-core/src/server-functions-handler.ts

# Search for getDefaultSerovalPlugins in the client-core package
find packages/start-client-core -name '*.ts' -o -name '*.tsx' | head -20

# Look for any documentation or type definitions for SerovalPlugin
rg -n 'SerovalPlugin' packages/start-client-core -t ts -A 2 -B 1 | head -40

Repository: TanStack/router

Length of output: 4979


🏁 Script executed:

#!/bin/bash
# Check the actual seroval Plugin type definition
rg -n 'Plugin' node_modules/seroval/package.json 2>/dev/null || echo "Checking alternative"

# Look for the actual type parameters being used in the parsePayload function
sed -n '65,75p' packages/start-server-core/src/server-functions-handler.ts

# Check if there are any other type safety issues in the file
rg -n ': any' packages/start-server-core/src/server-functions-handler.ts

# Look at how parsePayload uses the plugins
sed -n '65,95p' packages/start-server-core/src/server-functions-handler.ts

Repository: TanStack/router

Length of output: 1841


Module-level caching is correct, but TypeScript strict mode compliance is violated throughout the file.

The lazy initialization and caching of serovalPlugins is a safe optimization—the plugins are stateless serialization adapters created from configuration. However, the file extensively violates the coding guideline requiring "TypeScript strict mode with extensive type safety":

  • Line 17: SerovalPlugin<any, any> uses any type parameters
  • Lines 30, 64, 114, 148, 154, 156, 229, 281: Multiple function parameters and variables typed as any

Replace all any type annotations with proper types. For serovalPlugins, use explicit generic parameters from the seroval library. For function parameters like context, payload, and error, provide concrete types instead of any.

🤖 Prompt for AI Agents
In packages/start-server-core/src/server-functions-handler.ts (lines 12–23 and
referenced lines 30, 64, 114, 148, 154, 156, 229, 281), replace all uses of the
any type with concrete types: change serovalPlugins from
Array<SerovalPlugin<any, any>> to an explicit generic such as
Array<SerovalPlugin<unknown, unknown>> or the exact serialization input/output
types from the seroval lib (import and use those actual generics), and update
all function parameters and variables currently typed as any (context, payload,
error, etc.) to the precise project types (e.g., ServerContext,
FunctionInvokePayload, InvokeError or equivalent types exported by your
server/types module) — if those types don't yet exist, define narrow interfaces
for the shape of context, payload and error and import them into this file;
ensure all replacements remove any and use strict nullability/readonly
annotations as appropriate so the file compiles under TypeScript strict mode.

Comment on lines +110 to 120
if (methodLower === 'get') {
// Get payload directly from searchParams
const payloadParam = url.searchParams.get('payload')
// If there's a payload, we should try to parse it
payload = payload ? parsePayload(JSON.parse(payload)) : {}
const payload: any = payloadParam
? parsePayload(JSON.parse(payloadParam))
: {}
payload.context = { ...context, ...payload.context }
// Send it through!
return await action(payload, signal)
}
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

Add error handling for JSON.parse to prevent unhandled exceptions.

Line 115 calls JSON.parse(payloadParam) without error handling. If the payload query parameter contains malformed JSON, this will throw an uncaught exception during request processing.

🔎 Proposed fix with error handling
 if (methodLower === 'get') {
   // Get payload directly from searchParams
   const payloadParam = url.searchParams.get('payload')
   // If there's a payload, we should try to parse it
-  const payload: any = payloadParam
-    ? parsePayload(JSON.parse(payloadParam))
-    : {}
+  let payload: any = {}
+  if (payloadParam) {
+    try {
+      payload = parsePayload(JSON.parse(payloadParam))
+    } catch (error) {
+      throw new Error('Invalid JSON payload in query parameter', { cause: error })
+    }
+  }
   payload.context = { ...context, ...payload.context }
   // Send it through!
   return await action(payload, signal)
 }
🤖 Prompt for AI Agents
In packages/start-server-core/src/server-functions-handler.ts around lines 110
to 120, the code calls JSON.parse(payloadParam) directly which can throw on
malformed JSON; wrap the JSON.parse call in a try/catch, and on parse failure
handle it gracefully (e.g., log the error and return a controlled client
error/response or throw a specific 400 error with a descriptive message like
"Invalid payload JSON"), only call parsePayload and merge context when parsing
succeeds, and ensure the original flow returns the action(payload, signal) on
success.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/start-server-core/src/server-functions-handler.ts (1)

117-133: Inconsistent context merge order between GET and POST requests.

The context merging logic differs between request methods:

  • Line 117 (GET): { ...context, ...payload.context } — payload context takes precedence
  • Line 132 (POST): { ...payload.context, ...context } — server context takes precedence

This inconsistency could lead to different behavior depending on the HTTP method used. Verify this is intentional, otherwise unify the merge order.

🔎 Proposed fix to make POST consistent with GET
       const payload = jsonPayload ? parsePayload(jsonPayload) : {}
-      payload.context = { ...payload.context, ...context }
+      payload.context = { ...context, ...payload.context }
       return await action(payload, signal)
♻️ Duplicate comments (4)
packages/start-server-core/src/server-functions-handler.ts (4)

16-23: Module-level caching is a good optimization; TypeScript strict mode issue already flagged.

The lazy caching pattern for serovalPlugins and the FORM_DATA_CONTENT_TYPES constant extraction are appropriate optimizations. The any type parameters on line 17 were already flagged in a previous review.

Consider adding as const to FORM_DATA_CONTENT_TYPES for stricter typing:

 const FORM_DATA_CONTENT_TYPES = [
   'multipart/form-data',
   'application/x-www-form-urlencoded',
-]
+] as const

57-67: Lazy initialization pattern is correct.

The lazy initialization of serovalPlugins is safe in JavaScript's single-threaded event loop and avoids repeated calls to getDefaultSerovalPlugins(). The parsePayload helper cleanly encapsulates the deserialization logic.

The any type annotations in the function signature and return type were already flagged in the previous review.


110-120: JSON.parse error handling issue already flagged.

The lack of error handling for JSON.parse(payloadParam) on line 115 was already identified in the previous review. Malformed JSON in the query parameter will throw an uncaught exception.


281-291: Helper function is correct.

The isNotFoundResponse function properly constructs a 404 response with the error payload. The defensive spread pattern ...(headers || {}) handles potentially undefined headers correctly.

The any parameter type was already flagged in the previous review.

🧹 Nitpick comments (4)
packages/start-server-core/src/server-functions-handler.ts (3)

91-104: Consider logging parse failures for serialized context.

The empty catch block at line 103 silently swallows JSON.parse errors for the serialized context. While this defensive approach prevents crashes from malformed context, it may hide issues during debugging.

🔎 Proposed improvement
           try {
             const parsedContext = JSON.parse(serializedContext)
             const deserializedContext = fromJSON(parsedContext, {
               plugins: serovalPlugins,
             })
             if (
               typeof deserializedContext === 'object' &&
               deserializedContext
             ) {
               params.context = { ...context, ...deserializedContext }
             }
-          } catch {}
+          } catch (e) {
+            // Log but continue - malformed context shouldn't fail the request
+            console.warn('Failed to parse serialized context:', e)
+          }

150-193: Streaming optimization pattern is well-designed.

The approach of first attempting synchronous serialization and falling back to streaming only when needed is efficient. The callback reassignment pattern is unconventional but correct.

Minor nit: the type assertion on line 152 is redundant.

🔎 Remove redundant type assertion
-        let done = false as boolean
+        let done = false

233-239: Remove commented-out code.

The commented-out conditional block appears to be dead code. If it's no longer needed after this refactor, remove it to improve readability. If it might be needed in the future, consider documenting why it was removed.

🔎 Proposed removal
     } catch (error: any) {
       if (error instanceof Response) {
         return error
       }
-      // else if (
-      //   isPlainObject(error) &&
-      //   'result' in error &&
-      //   error.result instanceof Response
-      // ) {
-      //   return error.result
-      // }

       // Currently this server-side context has no idea how to
packages/start-client-core/src/client-rpc/serverFnFetcher.ts (1)

147-157: Use console.error for error logging.

Line 154 uses console.log(error) when logging an error before rethrowing. For consistency and proper error severity, this should be console.error(error).

🔎 Suggested fix
   } catch (error) {
     if (error instanceof Response) {
       response = error
     } else {
-      console.log(error)
+      console.error(error)
       throw error
     }
   }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fc114d8 and bd33dee.

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

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript strict mode with extensive type safety for all code

Files:

  • packages/start-server-core/src/server-functions-handler.ts
  • packages/start-client-core/src/client-rpc/serverFnFetcher.ts
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Implement ESLint rules for router best practices using the ESLint plugin router

Files:

  • packages/start-server-core/src/server-functions-handler.ts
  • packages/start-client-core/src/client-rpc/serverFnFetcher.ts
🧠 Learnings (4)
📚 Learning: 2025-12-06T15:03:07.223Z
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-06T15:03:07.223Z
Learning: Applies to **/*.{ts,tsx} : Use TypeScript strict mode with extensive type safety for all code

Applied to files:

  • packages/start-server-core/src/server-functions-handler.ts
📚 Learning: 2025-12-06T15:03:07.223Z
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-06T15:03:07.223Z
Learning: Applies to **/*.{js,ts,tsx} : Implement ESLint rules for router best practices using the ESLint plugin router

Applied to files:

  • packages/start-server-core/src/server-functions-handler.ts
📚 Learning: 2025-12-24T22:47:44.320Z
Learnt from: schiller-manuel
Repo: TanStack/router PR: 6211
File: e2e/react-start/i18n-paraglide/src/server.ts:6-6
Timestamp: 2025-12-24T22:47:44.320Z
Learning: In TanStack Router projects using `inlang/paraglide-js`, the callback passed to `paraglideMiddleware` should use `() => handler.fetch(req)` (referencing the outer `req`) instead of `({ request }) => handler.fetch(request)`. This is intentional because the router needs the untouched URL to perform its own rewrite logic with `deLocalizeUrl`/`localizeUrl`. The middleware's processed request would delocalize the URL and interfere with the router's rewrite handling.

Applied to files:

  • packages/start-client-core/src/client-rpc/serverFnFetcher.ts
📚 Learning: 2025-08-30T09:12:13.852Z
Learnt from: Sheraff
Repo: TanStack/router PR: 5051
File: packages/router-core/src/utils.ts:310-315
Timestamp: 2025-08-30T09:12:13.852Z
Learning: In TanStack Router's deepEqual utility, using for..in instead of Object.keys() in getObjectKeys() when ignoreUndefined=true is acceptable because it's called only after isPlainObject() checks, which ensure objects have standard Object prototype chains with no inherited enumerable properties.

Applied to files:

  • packages/start-client-core/src/client-rpc/serverFnFetcher.ts
⏰ 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). (1)
  • GitHub Check: Test
🔇 Additional comments (7)
packages/start-server-core/src/server-functions-handler.ts (1)

1-12: Imports look good.

The type-only import for SerovalPlugin with aliasing is appropriate and follows TypeScript best practices.

packages/start-client-core/src/client-rpc/serverFnFetcher.ts (6)

15-27: LGTM!

The hasOwnProperties helper is well-implemented with an efficient short-circuit approach. The hasOwnProperty.call check correctly ensures only own enumerable properties are considered, consistent with similar patterns used elsewhere in TanStack Router. Based on learnings, this for...in pattern is acceptable when the context ensures proper object types.


39-42: Type inconsistency resolved.

The headers property is now correctly typed as optional (headers?: HeadersInit), matching the conditional check at line 45. This addresses the previous review feedback.


44-50: LGTM!

Clean header construction with proper conditional handling. The accept header is correctly set only for payload types.


52-68: LGTM!

The GET handling correctly rejects FormData (which requires a body) and properly serializes the payload into the query string. The URL construction logic handles both cases (with/without existing query params).


70-77: LGTM!

POST handling cleanly delegates to getFetchBody and properly sets the content-type header when available.


99-103: LGTM!

The combined check opts.context && hasOwnProperties(opts.context) correctly handles both null/undefined contexts and empty object contexts, ensuring only meaningful context data is serialized.

@schiller-manuel schiller-manuel merged commit 767e4a9 into main Dec 25, 2025
6 checks passed
@schiller-manuel schiller-manuel deleted the only-createServerFn branch December 25, 2025 23:12
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.

2 participants