Next.js の Server Action、結局いつ使うのか問題
結論から言う
用途が見つからなかった。
Server Action を「あえて選ぶ理由」を真剣に探したが、API Route で代替できないケースに行き着かなかった。その過程を書く。
Server Action とは
Next.js の App Router で導入されたサーバーサイド処理の仕組み。"use server" をつけた関数を Client Component から直接呼べる。
// app/actions.ts
"use server";
export async function createTool(data: FormData) {
await db.tools.create({ ... });
revalidatePath("/tools");
}
// コンポーネントから直接呼ぶ
<form action={createTool}>...</form>
HTTP エンドポイントを定義せずにサーバーで処理を走らせられる、というのが売り文句だ。
試した用途と、なぜ没にしたか
フォーム送信
最もよく挙げられるユースケース。確かに <form action={serverAction}> と書くだけで動くので手軽だ。
ただ POST /api/tools を叩く fetch を書いても大して変わらない。しかも API Route にしておけば他のサービスからも呼べるし、E2E テストで UI を介さずデータを組み立てられる。フォームのためだけに Server Action を選ぶ理由にはならなかった。
BFF(Backend for Frontend)
Next.js を BFF 層として使い、内部マイクロサービスを Server Action 経由で呼ぶパターンを考えた。
しかし API Route でも fetch で内部サービスを叩けばそのまま BFF になる。認証トークンの秘匿も API Route のサーバーサイドで処理できる。Server Action を使う理由がなかった。
DAL(Data Access Layer)パターン
Next.js 公式ドキュメントにこんな記述がある。
If you plan to use Server Actions and expose a public API, we recommend moving the core logic to a Data Access Layer and calling the same logic from both the Server Action and the API route.
(訳)Server Action を使いつつ public API も公開する予定なら、コアロジックを Data Access Layer に切り出して Server Action と API Route の両方から呼ぶことを推奨する。
...つまり最終的に API Route から呼ぶことを前提にしている。Server Action は添え物だった。
DAL は「いいとこ取り」に見えるが、実態は Server Action と API Route という2つのエントリーポイントを維持するための抽象化レイヤーを増やしているだけだ。Server Action が担う revalidatePath は router.refresh() や SWR / React Query の mutation パターンで代替できる。API Route に統一すれば DAL というレイヤー自体が不要になる。
モバイル対応
Capacitor(WebView ラッパー)を使えばブラウザエンジンが動くので Server Action も動く。
ただし本格的なモバイルアプリとして React Native に移行しようとした瞬間、Server Action は一切呼べなくなる。API Route なら HTTP で呼べるのでそのまま使える。「将来 React Native に移行する可能性があるなら API Route にしておく方が安全」という消極的な理由しか浮かばなかった。
唯一残った可能性
revalidatePath / revalidateTag だけを Server Action に書いて、ロジックは API Route に置く構成はありえる。
"use server";
export async function createToolAction(data: CreateToolInput) {
await fetch("/api/tools", { method: "POST", body: JSON.stringify(data) });
revalidatePath("/tools"); // ← これだけのために Server Action
}
ただこれも Server Action のために余分なレイヤーが増えるだけで、router.refresh() や revalidate オプションを使えば API Route だけでも解決できる。
まとめ
| ユースケース | Server Action | API Route |
|---|---|---|
| フォーム送信 | 書くのが楽 | fetch を書くだけ |
| BFF | できる | できる |
| 外部サービスから呼ぶ | ✗ | ✅ |
| モバイル(React Native) | ✗ | ✅ |
| E2E でデータ組み立て | ✗ | ✅ |
「Server Action でできて API Route でできないこと」が最後まで見つからなかった。API Route は Server Action ができることをすべてカバーしつつ、逆は成り立たない。
「server/client の型が一体で通る」という体験は Server Action の良さではある。ただしそれを突き詰めると、TanStack Start の Server Functions の方が設計として筋が通っていて、Next.js でその体験を得るための妥協案が Server Action、という見方もできる。
つまり Server Action は「API Route より便利にしようとしたが外部から呼べない」「server/client 一体感を出そうとしたが TanStack Start の方が上手くやっている」と、どちらの方向でも上位互換がある。
プロダクトが成長してマイクロサービス化・モバイル対応・外部連携が発生したとき、Server Action に CRUD を書いていると全部書き直しになる。最初から API Route に統一しておくのが無難という結論に落ち着いた。
Server Action の明確な用途を知っている方がいれば、ぜひコメントで教えてほしい。
この記事は Vottia のプロダクト開発の中で得た知見をまとめたものです。
参考
Discussion