mcpField
S
start-server-core

start-server-core

github.com/tanstack开发15.3k369 次安装4 天前更新
代码仓库

关于 start-server-core

面向 TanStack Start 应用开发者的服务端运行时,负责处理服务端函数、服务端路由和应用 SSR 请求,并提供当前请求上下文中的请求、响应及 Cookie 工具。

主要功能

  1. 01处理函数、路由与 SSR 的请求处理器
  2. 02请求与响应工具
  3. 03Cookie 管理辅助函数
  4. 04基于 AsyncLocalStorage 的请求上下文
  5. 05外部会话库集成基础工具

适用场景

  1. 01处理应用收到的请求
  2. 02读取请求标头和 Cookie
  3. 03设置响应标头和 Cookie
  4. 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 分三个阶段处理请求:

  1. 服务端函数分发 — 如果 URL 与服务端函数前缀(/_serverFn)匹配,则反序列化负载、运行全局请求中间件、执行服务端函数,并返回序列化结果。

  2. 服务端路由处理器 — 对于非服务端函数请求,将 URL 与带有 server.handlers 的路由进行匹配。运行路由中间件,然后运行匹配的 HTTP 方法处理器。处理器可以返回 Response,也可以调用 next() 以继续进入 SSR。

  3. 应用路由 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 — 服务端路由处理器

评论

支持 Markdown