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)が再フェッチされ、新しいデータで再描画されます。

トランザクションと再検証の注意点

高度なパターン:複数モデルの整合性チェック

複数テーブル間の依存関係を扱う場合、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を組み合わせることで、

を構築できます。
シンプルながら信頼性の高いアプリ設計の基本パターンです。