Prisma × Server Actionsで安全なCRUDを設計する

〜型安全・再利用性・単一責任を両立させる設計〜

はじめに

Next.jsのServer Actionsを使えば、APIルートを作らずにサーバーサイド処理を直接呼び出すことができます。
これをPrismaと組み合わせることで、型安全かつシンプルなCRUD設計が可能になります。

この記事では、Server ActionsとPrismaを組み合わせて、
現実的なCRUD構成(Create / Read / Update / Delete)をどのように安全に実装できるかを解説します。

基本構成

src/
  server/
    actions/
      users/
        createUser.ts
        updateUser.ts
        deleteUser.ts
  lib/
    prisma.ts

CREATE(登録)

// src/server/actions/users/createUser.ts
"use server";

import { prisma } from "@/lib/prisma";
import { revalidatePath } from "next/cache";

export async function createUser(formData: FormData) {
  const name = formData.get("name")?.toString();
  const email = formData.get("email")?.toString();

  if (!name || !email) return { success: false, message: "必須項目が未入力です" };

  await prisma.user.create({ data: { name, email } });

  revalidatePath("/users");
  return { success: true, message: "登録が完了しました" };
}

READ(取得)

// src/server/actions/users/getUsers.ts
"use server";

import { prisma } from "@/lib/prisma";

export async function getUsers() {
  return prisma.user.findMany({ orderBy: { createdAt: "desc" } });
}

Server Component内で直接呼び出せるため、fetch を使う必要はありません。

UPDATE(更新)

// src/server/actions/users/updateUser.ts
"use server";

import { prisma } from "@/lib/prisma";
import { revalidatePath } from "next/cache";

export async function updateUser(formData: FormData) {
  const id = formData.get("id")?.toString();
  const name = formData.get("name")?.toString();

  if (!id || !name) return { success: false };

  await prisma.user.update({
    where: { id },
    data: { name },
  });

  revalidatePath("/users");
  return { success: true };
}

DELETE(削除)

// src/server/actions/users/deleteUser.ts
"use server";

import { prisma } from "@/lib/prisma";
import { revalidatePath } from "next/cache";

export async function deleteUser(id: string) {
  await prisma.user.delete({ where: { id } });
  revalidatePath("/users");
}

エラーハンドリングのベストプラクティス

Server Actions内では、APIレスポンスのようにステータスコードを返せません。
そのため、構造化された戻り値オブジェクトで状態を明示するのがおすすめです。

return { success: false, message: "メールアドレスが重複しています" };

UI側ではこれを判定してエラーメッセージを表示します。

まとめ

Server Actions × Prismaを組み合わせることで:

シンプルながら堅牢なCRUD構成を構築できます。