「API Routeを書いてる奴、まだ2010年代の亡霊に取り憑かれてるの?」
Next.jsのApp Routerが登場して久しいが、いまだに律儀に`/api`ディレクトリを掘って、`fetch`を叩いて、JSONをパースして……なんて泥臭いことをやってるなら、今すぐその手を止めろ。Server Actionsは単なる「便利な機能」じゃない。RESTという呪縛から解放された、RPC(Remote Procedure Call)の現代的再実装だ。フロントエンドとバックエンドの境界線を物理的に消し飛ばす、この破壊的なパラダイムシフトに乗れないエンジニアは、これから確実に淘汰される。
1. Server Actionsは「楽」だが、死ぬほど「罠」だらけ
Server Actionsは一見すると魔法だ。Zodで型を縛り、そのままDBを叩く。これで十分シンプルに見える。だが、多くのエンジニアがここで「再検証(revalidatePath)のタイミング」で詰み、深夜3時に頭を抱えることになる。
// app/actions/user.ts
"use server";
import { z } from "zod";
import { db } from "@/lib/db";
// 戻り値の型を統一する。これがないとフロント側での型推論が死ぬ
export type ActionResponse<T> =
| { success: true; data: T }
| { success: false; error: string; fieldErrors?: Record<string, string[]> };
const schema = z.object({
username: z.string().min(3),
email: z.string().email(),
});
export async function createUser(prevState: any, formData: FormData): Promise<ActionResponse<{ id: string }>> {
const result = schema.safeParse(Object.fromEntries(formData));
if (!result.success) {
return { success: false, error: "バリデーションエラー", fieldErrors: result.error.flatten().fieldErrors };
}
try {
const user = await db.user.create({ data: result.data });
revalidatePath("/dashboard");
return { success: true, data: { id: user.id } };
} catch (e) {
// 実際にはここでSentry等にログを飛ばすのがプロの流儀
console.error("DB Error:", e);
return { success: false, error: "サーバー内部で予期せぬエラーが発生しました" };
}
}
2. キャッシュ地獄から脱出する「楽観的UI」の鉄則
以前、タスク管理アプリを構築した際、Server Actionsを実行した直後にUIが更新されないバグに3時間溶かした。原因は`revalidatePath`への過信だ。ネットワークのラグやキャッシュ戦略の裏をかかれ、ユーザーの目には「何も変わっていない画面」が映り続ける。
解決策は、`useOptimistic`による強引なUI反映だ。
// app/components/TodoForm.tsx
"use client";
import { useOptimistic } from "react";
import { addTodo } from "@/actions/todo";
export function TodoForm({ initialTodos }) {
const [optimisticTodos, addOptimisticTodo] = useOptimistic(
initialTodos,
(state, newTodo) => [...state, { ...newTodo, id: Math.random().toString() }]
);
async function action(formData: FormData) {
addOptimisticTodo({ title: formData.get("title") });
const res = await addTodo(formData);
if (!res.success) alert(res.error);
}
return <form action={action}>...</form>;
}
これを実装しないServer Actionsは「レスポンスが遅いクソアプリ」だ。UXを語るなら、ここを避けて通るな。
3. なぜ「API Route」への執着を捨てるべきか
「フロントとバックを疎結合にしたい」という幻想を捨てろ。GoogleやNetflixのような超巨大組織ならともかく、個人開発でAPIの型定義を別パッケージに切り出し、`trpc`や`OpenAPI`で整合性を保つ作業に費やす時間は、現代のエンジニアリングにおいて純粋な負債でしかない。
Server Actionsの真価は、DBスキーマ変更がUIに即座にコンパイルエラーとして伝播する「単一ソースコードの恩恵」にある。このスピード感に慣れると、もうREST APIなんて書く気になれないはずだ。
4. 限界を認め、プロとして立ち回れ
もちろん、Server Actionsは万能ではない。以下の現実と向き合う覚悟が必要だ。
- デバッグの闇: サーバーサイドのスタックトレースを追うには、`instrumentation.ts`を活用し、ログを構造化せよ。`console.log`で凌ぐのは学生レベルだ。
- エラーハンドリングの設計: 前述した`ActionResponse`のように、戻り値を型で縛り、クライアント側で網羅的な分岐を強制せよ。
- API Routeが必要なケース: 認証(Middlewareとの兼ね合い)や、Stripe等の外部サービスからのWebHook受信、あるいはモバイルアプリへのAPI提供など、「ブラウザ外からのアクセス」が必要な場合は迷わずAPI Routeを書け。それ以外はServer Actionsで十分だ。
結論:APIを捨てる覚悟を持て
結局のところ、Server Actionsは「フロントエンドエンジニアがバックエンドを触るための最適解」であり、「バックエンドエンジニアがフロントの面倒を見るための最適解」だ。
API Routeを書いていた時間を、UIの磨き込みやロジックのブラッシュアップに回せ。技術スタックを統合し、コンテキストスイッチを極限まで減らすこと。それこそが、個人の開発スピードを爆速にする唯一の道だ。
Next.jsの機能を使い倒せ。APIなんて、どうしても必要な時以外は書くな。