How to get router context in createServerFn in Tanstack Start?

Summary

A developer working with Tanstack Start wanted to access router context (specifically a sessionId) inside a createServerFn function. The confusion arose because router context is typically associated with the client-side router instance. To bridge this gap, the solution involves accessing the event object passed to the server function, which contains the request context. We solved this by using getRouterContext (or accessing the event context directly) to retrieve the state established during beforeLoad or load functions.

Root Cause

The root cause is a misunderstanding of the execution boundary in Tanstack Start.

  • Router Context is built and consumed on the Client Router via hooks like Route.useRouterContext().
  • Server Functions (createServerFn) are executed on the Server (or during SSR) and do not have direct access to the client-side React hooks or the client router instance.
  • The missing link is that the router context established during the Server-Side Rendering (SSR) phase (via load or beforeLoad) is serialized and injected into the server request payload.

Why This Happens in Real Systems

In modern Universal React architectures, Data Fetching and Server Actions must be decoupled from the UI component tree.

  • Separation of Concerns: Server functions are standalone endpoints. They should not rely on client-side closure variables that might be stale or non-existent on the server.
  • Request Context: Data required for the server function must be carried via the HTTP Request context (headers, cookies, or the serialized event object).
  • Mental Model Gap: Developers often view “Router Context” as a global singleton. In reality, it is a session-scoped state that must be passed explicitly from the router loader to the server function execution environment.

Real-World Impact

  • Authorization Failures: Without accessing router context in server functions, developers cannot validate User Roles or Session IDs for sensitive mutations.
  • Data Leakage: Developers might resort to passing sensitive data via URL params or query strings to get it into server functions, violating security best practices.
  • Boilerplate Code: Without a clean way to access this context, developers are forced to manually pass userId or sessionId as arguments to every single server function call, leading to “prop drilling” at the API level.

Example or Code

To access the router context (payloaded from load/beforeLoad) inside createServerFn, you must access the event context. Tanstack Start typically exposes the router context within the event object.

Here is the correct pattern to retrieve the context:

import { createServerFn } from '@tanstack/react-start'
import { getRouterContext } from '@tanstack/react-start/router'

// 1. Define the Server Function
export const myServerAction = createServerFn('POST', async (inputData, { request }) => {

  // 2. Access the Router Context (Specific to Tanstack Start)
  // This retrieves the context established in beforeLoad/load for this request
  const routerContext = getRouterContext()

  // 3. Extract your specific value (e.g., sessionId)
  const sessionId = routerContext?.sessionId

  if (!sessionId) {
    throw new Error('No Session ID found in Router Context')
  }

  // 4. Perform server logic
  return {
    receivedData: inputData,
    associatedSession: sessionId,
    timestamp: new Date().toISOString()
  }
})

How Senior Engineers Fix It

Senior engineers treat Router Context as a Request-scoped dependency.

  • Explicit Injection: They do not rely on implicit magic. They explicitly grab the event (or getRouterContext) at the top of the server function.
  • Validation: They validate the existence of the context immediately. If a server function requires a userId, the function should throw an error if that context is missing, rather than failing silently or returning generic errors.
  • Type Safety: They ensure the router context is typed correctly so that routerContext.sessionId is recognized as a string, not unknown.
  • Abstraction: If many server functions need the sessionId, a Senior Engineer creates a wrapper function or a utility to extract and validate the context automatically, preventing code duplication.

Why Juniors Miss It

  • Hook Dependency: Juniors are accustomed to accessing state via React Hooks (useState, useContext, Route.useRouterContext). They fail to realize that createServerFn is not a React component and cannot use hooks.
  • “Client-Server” Blurring: They assume that because they are “in the router,” the context is global. They don’t distinguish between the Client Router Instance (browser) and the Server Runtime (Node/Bun/Deno).
  • Documentation Skimming: They often miss the distinction between useRouterContext (Client Hook) and the underlying context payload available on the event object in the server environment.