start-server-core — Claude Code skill: what it does, how to install
Reference for TanStack Start's server runtime: the request handler, request and response helpers, cookies. Useful once the server side of Start is already in the app.
npx skills add TanStack/router --skill start-server-core -a claude-codeWhat start-server-core does
The skill covers the server runtime of TanStack Start. It documents createStartHandler, helpers for reading the request and writing the response, and cookie handling. Every helper is reachable from anywhere in the call stack through AsyncLocalStorage, so nothing has to be threaded through function arguments.
A request goes through three phases in order: server functions, server routes, then app SSR. The handler pairs with defaultStreamHandler, and transformAssets rewrites asset URLs when files come from a CDN. Import from @tanstack/<framework>-start/server only; the same functions throw outside a request context.
Start ships no session API, so a session library has to be attached. iron-session 9 takes Start's getCookie and setCookie as its read and write adapters. Read cookies, headers and the request URL inside the live request. Edge runtimes inject those per request, and a value captured at module scope will bleed between concurrent calls. Worth checking before you deploy.
What it helps with
- read the incoming request inside a server function
- set a status code and a custom header from
createServerFn - wire iron-session to Start's cookie helpers
- append
set-cookievalues from an external session library without dropping other cookies - keep request state from leaking between concurrent calls
Example prompts for the agent
- Show me how to read the session cookie inside a TanStack Start server function with
getCookie. - I need a 201 status and a
cache-control: no-storeheader fromcreateServerFn— which helpers in@tanstack/react-start/serverdo that? - Wire iron-session 9 to Start's
getCookieandsetCookie, then storeuserIdin the session.
How to install start-server-core
1Skills CLI (Vercel)
npx skills add TanStack/router --skill start-server-core -a claude-code2Manually: put the files in place
~/.claude/skills/start-server-core/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/start-server-core/
1Skills CLI (Vercel)
npx skills add TanStack/router --skill start-server-core -a codex2Manually: put the files in place
.agents/skills/start-server-core/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add TanStack/router --skill start-server-core -a cursor2Manually: put the files in place
.cursor/skills/start-server-core/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install TanStack/router start-server-core2Manually: put the files in place
.github/skills/start-server-core/SKILL.md1Skills CLI (Vercel)
npx skills add TanStack/router --skill start-server-core -a gemini-cli2Platform CLI
gemini skills install https://github.com/TanStack/router --path packages/start-server-core/skills/start-server-coreManually: put the files in place
.devin/skills/start-server-core/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add TanStack/router --skill start-server-core -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about start-server-core
How do I install start-server-core in Claude Code?
Run npx skills add TanStack/router --skill start-server-core -a claude-code. The skill file lands at ~/.claude/skills/start-server-core/SKILL.md.
What does start-server-core do?
It describes the server side of TanStack Start: createStartHandler, request and response helpers, cookie management. All of them are available through AsyncLocalStorage anywhere during request handling.
Do I still need a session library?
Yes. Start has no built-in session API; iron-session 9 plugs into getCookie and setCookie directly, and Fetch-style libraries are called from a server route and their response returned.
Source text
SKILL.md · MIT
---
name: start-server-core
description: >-
Server-side runtime for TanStack Start: createStartHandler,
request/response utilities (getRequest, setResponseHeader,
setCookie, getCookie, appendResponseHeader), three-phase request handling,
AsyncLocalStorage context.
metadata:
type: core
library: tanstack-start
library_version: '1.169.17'
sources:
- TanStack/router:packages/start-server-core/src
- TanStack/router:docs/start/framework/react/guide/server-entry-point.md
---
# Start 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.
```ts
// 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):
```ts
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
```ts
// 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
```ts
// 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:
```ts
appendResponseHeader('set-cookie', await externalSessionLib.commit())
```
## Cookie Management
```ts
// 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:
```ts
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 appropriate `SameSite`, `Path=/`, and `Secure` on 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:
1. **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.
2. **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 a `Response` or call `next()` to fall through to SSR.
3. **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.
```ts
// 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
© TanStack · MIT · TanStack/router