什麼是 Server Actions
傳統上,在 Next.js 中處理表單提交的流程是:
客戶端表單 → 發送 POST 請求 → API Route 處理 → 回傳結果 → 客戶端更新 UI
Server Actions 讓這個流程簡化為:
客戶端表單 → 直接呼叫伺服器函式 → UI 自動更新
Server Actions 是在伺服器端執行的非同步函式,帶有 use server 指令。你可以直接從 Server Component 的 JSX 中呼叫它,或從 Client Component 的 事件處理器中呼叫。
基本寫法
在 Server Component 中直接定義
在標有 async 的 Server Component 函式中,直接定義帶有 use server 指令的函式:
export default function ContactPage() {
async function handleSubmit(formData: FormData) {
'use server'
const email = formData.get('email') as string
const message = formData.get('message') as string
// 直接呼叫 Prisma 儲存到資料庫,不需要 API Route
await prisma.inquiry.create({ data: { email, message } })
// 讓快取失效,讓相關頁面重新渲染
revalidatePath('/admin/inquiries')
}
return (
<form action={handleSubmit}>
<input name="email" type="email" />
<textarea name="message" />
<button type="submit">送出</button>
</form>
)
}
獨立的 actions.ts 檔案(推薦)
把 Server Actions 集中在一個 actions.ts 檔案中,用 use server 指令標示整個檔案,讓 Client Components 也能 import 使用:
// app/actions.ts
'use server'
import { prisma } from '@/lib/prisma'
import { revalidatePath } from 'next/cache'
export async function createInquiry(formData: FormData) {
const email = formData.get('email') as string
await prisma.inquiry.create({ data: { email } })
revalidatePath('/admin/inquiries')
}
在 Client Component 中使用
Client Component 不能直接定義 Server Actions(因為它在瀏覽器執行),但可以 import 並呼叫來自 actions.ts 的 Server Actions:
'use client'
import { createInquiry } from './actions'
import { useTransition } from 'react'
export function ContactForm() {
const [isPending, startTransition] = useTransition()
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault()
const formData = new FormData(e.currentTarget)
startTransition(async () => {
await createInquiry(formData)
})
}
return (
<form onSubmit={handleSubmit}>
<button type="submit" disabled={isPending}>
{isPending ? '送出中...' : '送出'}
</button>
</form>
)
}
useTransition 讓你追蹤 Server Action 是否執行中,用 isPending 顯示載入狀態。
Server Actions 的安全注意事項
Server Actions 在後端執行,調用者是可以偽造的(不一定是你的前端)——任何人都可以用 curl 等工具直接呼叫 Server Actions。
必須驗證:
身份驗證:敏感操作(刪除資料、更新設定)在 Server Action 中一定要先驗證用戶是否登入。用 auth() 或 getSession() 確認用戶身份,未登入就拋出錯誤。
輸入驗證:永遠不要信任 formData 的輸入——用 Zod 等驗證庫驗證格式和內容,再進行資料庫操作。
速率限制:公開表單的 Server Action(如聯絡表單)應考慮加入速率限制(IP 或 session 限制),防止同一個 IP 大量提交。
useFormState 和表單錯誤回傳
Server Actions 可以回傳值,配合 useFormState hook 讓表單顯示伺服器端的驗證錯誤:
在 Server Action 中回傳錯誤訊息,在 Client Component 中用 useFormState 接收並顯示,這樣可以在不需要 try/catch 的情況下優雅地處理表單驗證錯誤。
適合和不適合用 Server Actions 的場景
適合:
- 表單提交(聯絡表單、登入、留言)
- 管理後台的 CRUD 操作
- 業務邏輯觸發(發送 Email、第三方 API 調用)
不適合:
- 複雜的 RESTful API(需要給外部系統使用)
- 需要特殊的 HTTP 方法或 Header 回應
- WebSocket 等即時通訊
對內部使用的操作,Server Actions 比 API Routes 更簡潔;對需要公開的 API,仍然用 App Router 的 route.ts 建立 API Route。