伺服器函式允許你定義僅在伺服器端執行的邏輯,可以從應用程式的任何地方呼叫——載入器、元件、鉤子或其他伺服器函式。它們在伺服器上執行,但可以從客戶端程式碼無縫呼叫。
import { createServerFn } from '@tanstack/react-start'
export const getServerTime = createServerFn().handler(async () => {
// This runs only on the server
return new Date().toISOString()
})
// Call from anywhere - components, loaders, hooks, etc.
const time = await getServerTime()
伺服器函式提供伺服器能力(資料庫訪問、環境變數、檔案系統),同時保持跨網路邊界的型別安全。
伺服器函式使用 createServerFn() 建立,並且可以指定 HTTP 方法
import { createServerFn } from '@tanstack/react-start'
// GET request (default)
export const getData = createServerFn().handler(async () => {
return { message: 'Hello from server!' }
})
// POST request
export const saveData = createServerFn({ method: 'POST' }).handler(async () => {
// Server-only logic
return { success: true }
})
從以下位置呼叫伺服器函式
// In a route loader
export const Route = createFileRoute('/posts')({
loader: () => getPosts(),
})
// In a component
function PostList() {
const getPosts = useServerFn(getServerPosts)
const { data } = useQuery({
queryKey: ['posts'],
queryFn: () => getPosts(),
})
}
對於較大的應用程式,請考慮將伺服器端程式碼組織到單獨的檔案中。這裡有一種方法
src/utils/
├── users.functions.ts # Server function wrappers (createServerFn)
├── users.server.ts # Server-only helpers (DB queries, internal logic)
└── schemas.ts # Shared validation schemas (client-safe)
// users.server.ts - Server-only helpers
import { db } from '~/db'
export async function findUserById(id: string) {
return db.query.users.findFirst({ where: eq(users.id, id) })
}
// users.functions.ts - Server functions
import { createServerFn } from '@tanstack/react-start'
import { findUserById } from './users.server'
export const getUser = createServerFn({ method: 'GET' })
.inputValidator((data: { id: string }) => data)
.handler(async ({ data }) => {
return findUserById(data.id)
})
伺服器函式可以靜態匯入到任何檔案,包括客戶端元件
// ✅ Safe - build process handles environment shaking
import { getUser } from '~/utils/users.functions'
function UserProfile({ id }) {
const { data } = useQuery({
queryKey: ['user', id],
queryFn: () => getUser({ data: { id } }),
})
}
構建過程會將客戶端包中的伺服器函式實現替換為 RPC 存根。實際的伺服器程式碼永遠不會到達瀏覽器。
避免動態匯入伺服器函式
// ❌ Can cause bundler issues
const { getUser } = await import('~/utils/users.functions')
伺服器函式接受單個 data 引數。由於它們跨越網路邊界,因此驗證可以確保型別安全和執行時正確性。
import { createServerFn } from '@tanstack/react-start'
export const greetUser = createServerFn({ method: 'GET' })
.inputValidator((data: { name: string }) => data)
.handler(async ({ data }) => {
return `Hello, ${data.name}!`
})
await greetUser({ data: { name: 'John' } })
對於強大的驗證,請使用 Zod 等模式庫
import { createServerFn } from '@tanstack/react-start'
import { z } from 'zod'
const UserSchema = z.object({
name: z.string().min(1),
age: z.number().min(0),
})
export const createUser = createServerFn({ method: 'POST' })
.inputValidator(UserSchema)
.handler(async ({ data }) => {
// data is fully typed and validated
return `Created user: ${data.name}, age ${data.age}`
})
使用 FormData 處理表單提交
export const submitForm = createServerFn({ method: 'POST' })
.inputValidator((data) => {
if (!(data instanceof FormData)) {
throw new Error('Expected FormData')
}
return {
name: data.get('name')?.toString() || '',
email: data.get('email')?.toString() || '',
}
})
.handler(async ({ data }) => {
// Process form data
return { success: true }
})
伺服器函式可以丟擲錯誤、重定向和未找到響應,這些錯誤在從路由生命週期或使用 useServerFn() 的元件呼叫時會自動處理。
import { createServerFn } from '@tanstack/react-start'
export const riskyFunction = createServerFn().handler(async () => {
if (Math.random() > 0.5) {
throw new Error('Something went wrong!')
}
return { success: true }
})
// Errors are serialized to the client
try {
await riskyFunction()
} catch (error) {
console.log(error.message) // "Something went wrong!"
}
使用重定向進行身份驗證、導航等
import { createServerFn } from '@tanstack/react-start'
import { redirect } from '@tanstack/react-router'
export const requireAuth = createServerFn().handler(async () => {
const user = await getCurrentUser()
if (!user) {
throw redirect({ to: '/login' })
}
return user
})
為缺失的資源丟擲未找到錯誤
import { createServerFn } from '@tanstack/react-start'
import { notFound } from '@tanstack/react-router'
export const getPost = createServerFn()
.inputValidator((data: { id: string }) => data)
.handler(async ({ data }) => {
const post = await db.findPost(data.id)
if (!post) {
throw notFound()
}
return post
})
有關更高階的伺服器函式模式和功能,請參閱這些專用指南
訪問請求標頭、cookie 並自定義響應
import { createServerFn } from '@tanstack/react-start'
import {
getRequest,
getRequestHeader,
setResponseHeaders,
setResponseStatus,
} from '@tanstack/react-start/server'
export const getCachedData = createServerFn({ method: 'GET' }).handler(
async () => {
// Access the incoming request
const request = getRequest()
const authHeader = getRequestHeader('Authorization')
// Set response headers (e.g., for caching)
setResponseHeaders(
new Headers({
'Cache-Control': 'public, max-age=300',
'CDN-Cache-Control': 'max-age=3600, stale-while-revalidate=600',
}),
)
// Optionally set status code
setResponseStatus(200)
return fetchData()
},
)
可用實用程式
從伺服器函式流式傳輸型別資料到客戶端。請參閱從伺服器函式流式傳輸資料指南。
返回 Response 物件二進位制資料或自定義內容型別。
透過使用 HTML 表單中的 .url 屬性,可以在沒有 JavaScript 的情況下使用伺服器函式。
使用中介軟體組合伺服器函式,用於身份驗證、日誌記錄和共享邏輯。請參閱中介軟體指南。
在構建時快取伺服器函式的結果以進行靜態生成。請參閱靜態伺服器函式。
使用 AbortSignal 處理長時間執行的操作的請求取消。
伺服器函式在底層由生成的、穩定的函式 ID 定址。這些 ID 嵌入到客戶端/SSR 構建中,並由伺服器用於在執行時定位和匯入正確的模組。
預設情況下,ID 是相同種子生成的 SHA256 雜湊,以保持捆綁包緊湊並避免洩露檔案路徑。如果兩個伺服器函式最終具有相同的 ID(包括使用自定義生成器時),系統將透過附加遞增字尾(如 _1、_2 等)進行去重。
自定義
可以透過在配置 TanStack Start Vite 外掛時提供 generateFunctionId 函式來自定義生產構建的函式 ID 生成。
優先使用確定性輸入(檔名 + 函式名),以便 ID 在構建之間保持穩定。
請注意,此自定義是實驗性的,可能會發生變化。
示例
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
export default defineConfig({
plugins: [
tanstackStart({
serverFns: {
generateFunctionId: ({ filename, functionName }) => {
// Return a custom ID string
return crypto
.createHash('sha1')
.update(`${filename}--${functionName}`)
.digest('hex')
// If you return undefined, the default is used
// return undefined
},
},
}),
react(),
],
})
注意:伺服器函式使用提取伺服器程式碼並將其從客戶端包中分離出來的編譯過程,同時保持無縫的呼叫模式。在客戶端,呼叫變為向伺服器傳送 fetch 請求。