About start-server-core
A server-side runtime for TanStack Start, intended for developers building Start applications. It handles incoming requests through server functions, server routes, and app SSR, and provides request, response, and cookie utilities for active request contexts.
Key features
- 01Request handler for functions, routes, and SSR
- 02Request and response utilities
- 03Cookie management helpers
- 04AsyncLocalStorage request context
- 05External session library integration primitives
Use cases
- 01Handling incoming application requests
- 02Reading request headers and cookies
- 03Setting response headers and cookies
- 04Connecting an external session library
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ tanstack/ router --skill start-server-coreAfter installing, type/start-server-corein Claude Code to confirm it loaded.
Details
From the project READMEStart Server Core (@tanstack/start-server-core)
Server-side runtime for TanStack Start. Provides the request handler, request/response utilities, cookie management, and integration primitives for external session libraries. All utilities are available anywhere in the call stack during a request via AsyncLocalStorage.
CRITICAL: These utilities are SERVER-ONLY. Import them from
@tanstack/<framework>-start/server, not from the main entry point. They throw if called outside a server request context.CRITICAL: Types are FULLY INFERRED. Never cast, never annotate inferred values.
CRITICAL: Read cookies, headers, request URLs, and runtime environment values inside the active request. Do not capture them at module scope; edge runtimes may inject them per request, and concurrent requests must never share request-derived state.
createStartHandler
Creates the main request handler that processes all incoming requests through three phases: server functions, server routes, then app SSR.
// src/server.ts
// Use @tanstack/<framework>-start for your framework (react, solid, vue)
import { createStartHandler } from '@tanstack/react-start/server'
import { defaultStreamHandler } from '@tanstack/react-start/server'
export default createStartHandler({
handler: defaultStreamHandler,
})
With asset URL transforms (CDN):
export default createStartHandler({
handler: defaultStreamHandler,
transformAssets: 'https://cdn.example.com',
})
Request Utilities
All imported from @tanstack/<framework>-start/server. Available anywhere during request handling — no parameter passing needed.
Reading Request Data
// Use @tanstack/<framework>-start for your framework (react, solid, vue)
import { createServerFn } from '@tanstack/react-start'
import {
getRequest,
getRequestHeaders,
getRequestHeader,
getRequestIP,
getRequestHost,
getRequestUrl,
getRequestProtocol,
} from '@tanstack/react-start/server'
const serverFn = createServerFn({ method: 'GET' }).handler(async () => {
const request = getRequest()
const headers = getRequestHeaders()
const auth = getRequestHeader('authorization')
const ip = getRequestIP({ xForwardedFor: true })
const host = getRequestHost()
const url = getRequestUrl()
const protocol = getRequestProtocol()
return { ip, host }
})
Setting Response Data
// Use @tanstack/<framework>-start for your framework (react, solid, vue)
import { createServerFn } from '@tanstack/react-start'
import {
setResponseHeader,
setResponseHeaders,
setResponseStatus,
getResponseHeaders,
getResponseHeader,
getResponseStatus,
appendResponseHeader,
removeResponseHeader,
clearResponseHeaders,
} from '@tanstack/react-start/server'
const serverFn = createServerFn({ method: 'POST' }).handler(async () => {
setResponseStatus(201)
setResponseHeader('x-custom', 'value')
setResponseHeaders({ 'cache-control': 'no-store' })
return { created: true }
})
getResponseHeader and getResponseHeaders are read helpers. Treat the Headers returned by getResponseHeaders() as a snapshot; mutating it does not change the outgoing response. Use the setter/removal helpers for writes.
appendResponseHeader(name, value) appends without replacing existing values. For set-cookie it accepts fully serialized cookie strings and merges them by cookie identity (name + domain + path; a value cookie-es cannot parse is identified by its exact string) — the primitive for bridging external session/auth libraries:
appendResponseHeader('set-cookie', await externalSessionLib.commit())
Cookie Management
// Use @tanstack/<framework>-start for your framework (react, solid, vue)
import { createServerFn } from '@tanstack/react-start'
import {
getCookies,
getCookie,
setCookie,
deleteCookie,
} from '@tanstack/react-start/server'
const serverFn = createServerFn({ method: 'POST' }).handler(async () => {
const allCookies = getCookies()
const token = getCookie('session-token')
setCookie('preference', 'dark', {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 60 * 60 * 24 * 30, // 30 days
path: '/',
})
deleteCookie('old-cookie')
})
External Session Libraries
Start has no built-in session API. Use an external library and connect it to the active request using the cookie helpers, serialized headers, or a Fetch handler.
For a cookie adapter, iron-session 9 accepts Start's cookie functions directly:
import { getIronSession } from 'iron-session'
import { getCookie, setCookie } from '@tanstack/react-start/server'
// This is an application helper, not a Start export.
export function getAppSession() {
const password = process.env.SESSION_SECRET
if (!password || password.length < 32) {
throw new Error('SESSION_SECRET must be at least 32 characters')
}
return getIronSession<{ userId: string }>(
{ read: getCookie, write: setCookie },
{
cookieName: 'app-session',
password,
ttl: 7 * 24 * 60 * 60,
cookieOptions: {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/',
},
},
)
}
// After validating credentials in a server function:
const session = await getAppSession()
session.userId = user.id
await session.save()
// In a logout handler:
const logoutSession = await getAppSession()
logoutSession.destroy()
Use session.userId, with an absent-user check, when reading. Keep one session object per operation, or pass it from middleware through request context. The adapter reads the incoming cookie; calling it again does not read pending response cookies. Await writes before returning or redirecting. Do not put a session object or request-derived configuration in module scope.
iron-session checks ttl when unsealing, and each save renews that lifetime. Choose and test expiration, rotation, and storage behavior explicitly.
For libraries that accept a Fetch Request and return a Response, pass the current request and return the library's response from a server route. For cookie-only transfer, append each value from response.headers.getSetCookie() separately; commas can occur inside an Expires attribute. Do not use setResponseHeader('set-cookie', ...) to append alongside unrelated cookies.
Production Session Rules
- Keep credentials and session objects in server code; use a new object for each request.
- Load current account permissions from the authoritative store. A client route guard does not authorize a server function.
- Use server-side session records for immediate revocation, device tracking, or large data. Deleting a cookie does not invalidate copies of stateless credentials.
- Use
HttpOnly, an appropriateSameSite,Path=/, andSecureon HTTPS deployments. Cookie flags complement CSRF protection. - Test login, authenticated SSR and navigation, persistence before redirects, expiry, logout, and concurrent requests. Start handles request/response plumbing; the application and its session library own session semantics.
How Request Handling Works
createStartHandler processes requests in three phases:
-
Server Function Dispatch — If URL matches the server function prefix (
/_serverFn), deserializes the payload, runs global request middleware, executes the server function, and returns the serialized result. -
Server Route Handler — For non-server-function requests, matches the URL against routes with
server.handlers. Runs route middleware, then the matched HTTP method handler. Handlers can return aResponseor callnext()to fall through to SSR. -
App Router SSR — Loads all route loaders, dehydrates state for client hydration, and calls the handler callback (e.g.,
defaultStreamHandler) to render HTML.
Common Mistakes
1. CRITICAL: Importing server utilities in client code
Server utilities use AsyncLocalStorage and only work during server request handling. Importing them in client code causes build errors or runtime crashes.
// WRONG — importing in a component file that runs on client
import { getCookie } from '@tanstack/react-start/server'
function MyComponent() {
const token = getCookie('auth') // crashes on client
}
// CORRECT — use inside server functions only
// Use @tanstack/<framework>-start for your framework (react, solid, vue)
import { createServerFn } from '@tanstack/react-start'
import { getCookie } from '@tanstack/react-start/server'
const getAuth = createServerFn({ method: 'GET' }).handler(async () => {
return getCookie('auth')
})
2. HIGH: Forgetting to persist an external session
Mutating a session object does not necessarily write a cookie. Follow the library's explicit save or commit API and await it before returning or redirecting. Append serialized Set-Cookie values individually, preserving cookies from other middleware.
3. MEDIUM: Using session without HTTPS in production
Session cookies should use secure: true in production. The default cookie options may not enforce this.
4. CRITICAL: Capturing request or environment state at module scope
Do not create session config from process.env at module load or cache getRequest(), headers, cookies, or session data in a module variable. Create config and read request state inside the handler or middleware callback. This is required for per-request edge environments and prevents cross-request data leaks.
Cross-References
@tanstack/start-client-core#start-core/server-functions— creating server functions that use these utilities@tanstack/start-client-core#start-core/middleware— request middleware@tanstack/start-client-core#start-core/server-routes— server route handlers