PrismaのトランザクションとNext.jsの再検証戦略(revalidatePath)
〜整合性を壊さずにUIを即時反映させる設計〜
はじめに
複数のDB操作を1つの処理としてまとめたいとき、Prismaの$transactionが役立ちます。
また、Next.jsのrevalidatePathを組み合わせることで、UIとDBの状態を同期できます。
トランザクションの基本構文
await prisma.$transaction(async (tx) => {
await tx.user.create(...);
await tx.profile.create(...);
});
txオブジェクトはトランザクション内専用のPrismaClientです。
途中でエラーが発生すると、すべて自動でロールバックされます。
実践例:ユーザーと関連データを同時作成
// src/server/actions/createUserWithProfile.ts
"use server";
import { prisma } from "@/lib/prisma";
import { revalidatePath } from "next/cache";
export async function createUserWithProfile(formData: FormData) {
const name = formData.get("name")?.toString();
const email = formData.get("email")?.toString();
await prisma.$transaction(async (tx) => {
const user = await tx.user.create({
data: { name, email },
});
await tx.profile.create({
data: { userId: user.id, bio: "自己紹介未設定" },
});
});
revalidatePath("/users");
}
これで、ユーザーとプロフィールを1つのトランザクションで確実に作成できます。
revalidatePathの仕組み
revalidatePath()は、Next.js App Routerのキャッシュ再検証機能です。
サーバーアクションでデータを変更した後に呼び出すと、
該当ルート(例:/users)が再フェッチされ、新しいデータで再描画されます。
トランザクションと再検証の注意点
$transaction内でrevalidatePathを呼ばない(完了後に実行)- トランザクション完了前にUIを更新しない
- 並行更新の可能性がある場合は、DB側の制約も活用する(例:
@unique)
高度なパターン:複数モデルの整合性チェック
複数テーブル間の依存関係を扱う場合、Zodで事前バリデーションを行い、
トランザクションでは「必ず失敗しない状態」で実行するのが安全です。
const dataSchema = z.object({
name: z.string(),
email: z.string().email(),
});
const parsed = dataSchema.safeParse(input);
if (!parsed.success) throw new Error("入力不正");
まとめ
Prismaの$transactionとNext.jsのrevalidatePathを組み合わせることで、
- DB整合性を壊さずに
- UIを即時反映できる
- 安全な状態更新フロー
を構築できます。
シンプルながら信頼性の高いアプリ設計の基本パターンです。