关于 start-server-core
面向 TanStack Start 应用开发者的服务端运行时,负责处理服务端函数、服务端路由和应用 SSR 请求,并提供当前请求上下文中的请求、响应及 Cookie 工具。
主要功能
- 01处理函数、路由与 SSR 的请求处理器
- 02请求与响应工具
- 03Cookie 管理辅助函数
- 04基于 AsyncLocalStorage 的请求上下文
- 05外部会话库集成基础工具
适用场景
- 01处理应用收到的请求
- 02读取请求标头和 Cookie
- 03设置响应标头和 Cookie
- 04连接外部会话库
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ tanstack/ router --skill start-server-core安装后在 Claude Code 中输入/start-server-core确认已加载。
详细介绍
Start Server Core (@tanstack/start-server-core)
TanStack Start 的服务端运行时。提供请求处理器、请求/响应工具、Cookie 管理,以及与外部会话库集成的基础功能。在请求期间,可通过 AsyncLocalStorage 在调用栈的任意位置使用所有工具。
CRITICAL:这些工具仅限服务端使用。请从
@tanstack/<framework>-start/server导入,而不是从主入口导入。在服务器请求上下文之外调用时,这些工具会抛出错误。CRITICAL:类型完全由系统推断。切勿进行类型转换,也不要为推断出的值添加类型注解。
CRITICAL:请在当前请求中读取 Cookie、标头、请求 URL 和运行时环境值。不要在模块作用域捕获它们;边缘运行时可能会按请求注入这些值,并发请求绝不能共享源自请求的状态。
createStartHandler
创建主请求处理器,按三个阶段处理所有传入请求:服务端函数、服务端路由,然后是应用 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,
})
使用资源 URL 转换(CDN)时:
export default createStartHandler({
handler: defaultStreamHandler,
transformAssets: 'https://cdn.example.com',
})
请求工具
均从 @tanstack/<framework>-start/server 导入。在请求处理期间的任意位置均可使用,无需传递参数。
读取请求数据
// 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 }
})
设置响应数据
// 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 和 getResponseHeaders 是读取辅助函数。请将 getResponseHeaders() 返回的 Headers 视为快照;修改它不会更改发出的响应。写入时请使用设置或移除辅助函数。
appendResponseHeader(name, value) 会追加值,而不会替换已有值。对于 set-cookie,它接受完全序列化的 Cookie 字符串,并按 Cookie 身份(名称 + 域名 + 路径)合并;无法由 cookie-es 解析的值会按其精确字符串识别。它是对接外部会话/身份验证库的基础工具:
appendResponseHeader('set-cookie', await externalSessionLib.commit())
Cookie 管理
// 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')
})
外部会话库
Start 没有内置会话 API。请使用外部库,并通过 Cookie 辅助函数、序列化标头或 Fetch 处理器将其连接到当前请求。
对于 Cookie 适配器,iron-session 9 可直接接受 Start 的 Cookie 函数:
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()
读取时请使用 session.userId,并检查用户是否缺失。每次操作使用一个会话对象,或通过请求上下文从中间件传递会话对象。适配器会读取传入的 Cookie;再次调用它不会读取待发送的响应 Cookie。返回或重定向前,请等待写入完成。不要在模块作用域中存放会话对象或源自请求的配置。
iron-session 在解封时会检查 ttl,每次保存都会续期。请明确选择并测试过期、轮换和存储行为。
对于接受 Fetch Request 并返回 Response 的库,请传入当前请求,并从服务端路由返回该库的响应。若只传递 Cookie,请分别追加 response.headers.getSetCookie() 中的每个值;Expires 属性中可能包含逗号。不要使用 setResponseHeader('set-cookie', ...) 来追加 Cookie,以免覆盖其他 Cookie。
生产环境会话规则
- 将凭据和会话对象保留在服务端代码中;每个请求都使用新对象。
- 从权威存储加载当前账户权限。客户端路由守卫不能为服务端函数授权。
- 如需立即撤销、设备跟踪或存储大量数据,请使用服务端会话记录。删除 Cookie 不会使无状态凭据的副本失效。
- 在 HTTPS 部署中使用
HttpOnly、适当的SameSite、Path=/和Secure。Cookie 标志是 CSRF 防护的补充。 - 测试登录、已验证状态下的 SSR 和导航、重定向前的持久化、过期、登出以及并发请求。Start 负责请求/响应处理;应用和会话库负责会话语义。
请求处理的工作方式
createStartHandler 分三个阶段处理请求:
-
服务端函数分发 — 如果 URL 与服务端函数前缀(
/_serverFn)匹配,则反序列化负载、运行全局请求中间件、执行服务端函数,并返回序列化结果。 -
服务端路由处理器 — 对于非服务端函数请求,将 URL 与带有
server.handlers的路由进行匹配。运行路由中间件,然后运行匹配的 HTTP 方法处理器。处理器可以返回Response,也可以调用next()以继续进入 SSR。 -
应用路由 SSR — 加载所有路由加载器,为客户端水合而去水合状态,并调用处理器回调(例如
defaultStreamHandler)来渲染 HTML。
常见错误
1. 严重:在客户端代码中导入服务端实用工具
服务端实用工具使用 AsyncLocalStorage,且仅在服务端请求处理期间有效。在客户端代码中导入会导致构建错误或运行时崩溃。
// 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. 高:忘记持久化外部会话
修改会话对象不一定会写入 Cookie。请使用库明确提供的保存或提交 API,并在返回或重定向前等待其完成。逐个附加序列化后的 Set-Cookie 值,同时保留其他中间件设置的 Cookie。
3. 中:在生产环境中未使用 HTTPS 就使用会话
生产环境中的会话 Cookie 应设置 secure: true。默认 Cookie 选项可能不会强制启用此设置。
4. 严重:在模块作用域捕获请求或环境状态
不要在模块加载时通过 process.env 创建会话配置,也不要将 getRequest()、标头、Cookie 或会话数据缓存在模块变量中。应在处理器或中间件回调中创建配置并读取请求状态。这是边缘环境按请求处理所必需的,也能防止请求间的数据泄漏。
交叉引用
@tanstack/start-client-core#start-core/server-functions— 创建使用这些实用工具的服务端函数@tanstack/start-client-core#start-core/middleware— 请求中间件@tanstack/start-client-core#start-core/server-routes— 服务端路由处理器