
設定・環境構築
Server Actions(“use server”)はNext.js App Routerで、クライアントからサーバー側の関数を直接呼び出せる仕組みです。Route Handlersと比較して、API設計やfetchラッパーが不要で、型安全に呼び出せるのが特徴です。
| 観点 | Server Actions | Route Handlers |
|---|---|---|
| 型安全 | ✅ 自動(関数の引数/戻り値がそのまま) | ❌ 手動でrequest/response型定義 |
| エンドポイント設計 | 不要 | 必要(/api/xxx) |
| 外部からのアクセス | ❌ できない | ✅ Webhook等で使える |
| Edge Runtime | △ 制限あり | ✅ 対応 |
| キャッシュ | なし | Cache-Control設定可 |
// services/character.ts
"use server";
import prisma from "@/lib/prisma";
export async function getCharacters(userId?: string) {
return prisma.character.findMany({
where: {
OR: [{ userId: null }, ...(userId ? [{ userId }] : [])],
},
orderBy: { createdAt: "asc" },
});
}
export async function getCharacterBySlug(slug: string) {
return prisma.character.findUnique({ where: { slug } });
}
シンプルなDB操作をそのまま関数として公開。クライアントからimportして呼ぶだけで型が通ります。
// services/chatHistory.ts
"use server";
export async function getOrCreateTodaySession(
userId: string,
characterId: string,
) {
const date = todayDate();
const existing = await prisma.chatSession.findUnique({
where: { userId_characterId_date: { userId, characterId, date } },
include: { messages: { orderBy: { createdAt: "asc" } } },
});
if (existing) return existing;
return prisma.chatSession.create({
data: { userId, characterId, date },
include: { messages: { orderBy: { createdAt: "asc" } } },
});
}
// Server ActionをqueryFnとして直接使用
const { data: characters } = useQuery({
queryKey: ["characters", userId],
queryFn: () => getCharacters(userId), // Server Actionを直接渡す
enabled: !!userId,
});
Server Actionsはただの非同期関数なので、TanStack QueryのqueryFnにそのまま渡せます。fetch URLの管理が不要で、戻り値の型も自動推論されます。
export async function saveMessage(
sessionId: string,
role: "user" | "assistant", // stringではなくユニオン型で制約
content: string,
) {
return prisma.chatMessage.create({
data: { sessionId, role, content },
});
}
Route HandlersだとリクエストボディのバリデーションにZod等が必要ですが、Server Actionsなら引数の型がそのまま制約になります。
内部CRUD → Server Actions、外部連携 → Route Handlers
TanStack QueryのqueryFnにServer Actionをそのまま渡せる
引数のユニオン型でRoute Handlersのバリデーション相当の安全性を確保
2026/3/19
2026/3/19
2026/3/19
2026/3/19
2026/3/19
2026/3/19
2026/3/19
2026/3/15
2026/3/15
2026/2/26