Next.jsでPrismaを使ったCRUD操作完全ガイド

2025-08-26

Next.jsアプリケーションでPrismaを使用してデータベースのCRUD(Create, Read, Update, Delete)操作を行う方法を、初心者向けに徹底解説します。このガイドでは、基本的な操作から実践的なテクニックまで、3000字を超える詳細な説明を行います。

はじめに:Prisma Clientの基本

前回のセットアップで準備したPrisma Clientを使用して、データベース操作を行います。まず、Prisma Clientの基本的な使い方を確認しましょう。

import prisma from '../lib/prisma'

// すべてのユーザーを取得
const allUsers = await prisma.user.findMany()

Prisma Clientは、モデルごとに自動生成されたメソッドを提供します。これらのメソッドは強力な型サポートがあり、コードを書く際に自動補完が効くのが特徴です。

CREATE操作 – データの作成

単一レコードの作成

createメソッドを使用して新しいレコードを追加します。

// 新しいユーザーを作成
const newUser = await prisma.user.create({
  data: {
    name: '山田太郎',
    email: 'taro@example.com',
  },
})
console.log(newUser)

この操作では:

  1. userテーブルに新しいレコードが追加されます
  2. 作成されたレコードが返されます(idやcreatedAtなど自動生成されたフィールドを含む)

複数レコードの作成

createManyを使用して一度に複数のレコードを追加できます。

// 複数のユーザーを一括作成
const createdUsers = await prisma.user.createMany({
  data: [
    { name: '鈴木一郎', email: 'suzuki@example.com' },
    { name: '佐藤花子', email: 'sato@example.com' },
  ],
  skipDuplicates: true, // 重複をスキップ
})
console.log(`作成されたユーザー数: ${createdUsers.count}`)

リレーションのあるデータの作成

関連するモデルと一緒にデータを作成することも可能です。

// ユーザーと投稿を同時に作成
const userWithPost = await prisma.user.create({
  data: {
    name: '田中健太',
    email: 'tanaka@example.com',
    posts: {
      create: {
        title: '初めての投稿',
        content: 'Prismaを使い始めました!',
      },
    },
  },
  include: {
    posts: true, // 作成された投稿も返す
  },
})

READ操作 – データの読み取り

すべてのレコードを取得

findManyメソッドでテーブルの全レコードを取得します。

// すべてのユーザーを取得
const allUsers = await prisma.user.findMany()

条件を指定して取得

whereオプションでフィルタリングが可能です。

// 公開済みの投稿のみ取得
const publishedPosts = await prisma.post.findMany({
  where: {
    published: true,
  },
})

特定のフィールドのみ選択

selectで必要なフィールドのみ取得できます。

// 名前とメールのみ取得
const users = await prisma.user.findMany({
  select: {
    name: true,
    email: true,
  },
})

単一レコードの取得

findUniqueで一意のレコードを取得します。

// IDでユーザーを検索
const user = await prisma.user.findUnique({
  where: {
    id: 1,
  },
})

リレーションを含めて取得

includeで関連データを一緒に取得できます。

// ユーザーとその投稿を取得
const userWithPosts = await prisma.user.findUnique({
  where: {
    id: 1,
  },
  include: {
    posts: true,
  },
})

ソートとページネーション

// 作成日降順で10件取得
const recentPosts = await prisma.post.findMany({
  orderBy: {
    createdAt: 'desc',
  },
  take: 10, // 取得件数
})

// ページネーション(カーソルベース)
const secondPage = await prisma.post.findMany({
  cursor: {
    id: 10, // 最後に取得したレコードのID
  },
  skip: 1, // カーソルのレコードをスキップ
  take: 5, // 1ページあたりの件数
})

UPDATE操作 – データの更新

単一レコードの更新

updateメソッドでレコードを更新します。

// ユーザー情報を更新
const updatedUser = await prisma.user.update({
  where: {
    id: 1,
  },
  data: {
    name: '山田太郎(更新)',
  },
})

複数レコードの更新

updateManyで条件に合致する複数レコードを更新します。

// すべての未公開の投稿を公開状態に
const updatedPosts = await prisma.post.updateMany({
  where: {
    published: false,
  },
  data: {
    published: true,
  },
})

カウンタのインクリメント

数値フィールドを簡単に増減できます。

// 投稿の閲覧数を増やす
await prisma.post.update({
  where: {
    id: 1,
  },
  data: {
    viewCount: {
      increment: 1, // 1増やす
    },
  },
})

リレーションの更新

関連データを同時に更新することも可能です。

// ユーザーとその投稿を更新
await prisma.user.update({
  where: {
    id: 1,
  },
  data: {
    name: '新しい名前',
    posts: {
      updateMany: {
        where: {
          published: false,
        },
        data: {
          published: true,
        },
      },
    },
  },
})

DELETE操作 – データの削除

単一レコードの削除

deleteメソッドでレコードを削除します。

// IDが1のユーザーを削除
await prisma.user.delete({
  where: {
    id: 1,
  },
})

複数レコードの削除

deleteManyで条件に合致する複数レコードを削除します。

// 公開済みの投稿をすべて削除
await prisma.post.deleteMany({
  where: {
    published: true,
  },
})

全レコードの削除

// すべての投稿を削除
await prisma.post.deleteMany()

トランザクション処理

複数の操作を1つのトランザクションとして実行できます。

// トランザクションを使用したユーザー移行
const transfer = await prisma.$transaction([
  prisma.user.delete({
    where: { id: 1 },
  }),
  prisma.user.create({
    data: {
      name: '新しいユーザー',
      email: 'new@example.com',
    },
  }),
])

エラーハンドリング

Prisma操作時のエラーを適切に処理しましょう。

try {
  const user = await prisma.user.create({
    data: {
      email: 'existing@example.com', // 既に存在する場合
    },
  })
} catch (error) {
  if (error instanceof Prisma.PrismaClientKnownRequestError) {
    if (error.code === 'P2002') {
      console.error('一意制約違反: このメールアドレスは既に使用されています')
    }
  }
  throw error
}

高度なクエリテクニック

集計関数

// 投稿数の集計
const postStats = await prisma.post.aggregate({
  _count: true, // 総数
  _avg: {
    viewCount: true, // 平均閲覧数
  },
  _max: {
    viewCount: true, // 最大閲覧数
  },
})

全文検索(フルテキストサーチ)

// 投稿内容で検索
const searchResults = await prisma.post.findMany({
  where: {
    content: {
      search: 'Prisma ORM', // 検索キーワード
    },
  },
})

生SQLクエリ

複雑なクエリが必要な場合、直接SQLを実行できます。

const result = await prisma.$queryRaw`
  SELECT * FROM "User" WHERE "name" LIKE ${'%山田%'}
`

Next.js APIルートでの使用例

実際にNext.jsのAPIルートでPrismaを使用する例を示します。

pages/api/users.js:

import prisma from '../../lib/prisma'

export default async function handler(req, res) {
  switch (req.method) {
    case 'GET':
      const users = await prisma.user.findMany()
      res.status(200).json(users)
      break
    case 'POST':
      const newUser = await prisma.user.create({
        data: req.body,
      })
      res.status(201).json(newUser)
      break
    default:
      res.setHeader('Allow', ['GET', 'POST'])
      res.status(405).end(`Method ${req.method} Not Allowed`)
  }
}

パフォーマンス最適化のヒント

  1. 必要なフィールドのみ選択:
   await prisma.user.findMany({
     select: { id: true, name: true },
   })
  1. バッチ処理の活用:
   // 悪い例: N+1問題
   for (const user of users) {
     const posts = await prisma.post.findMany({
       where: { authorId: user.id },
     })
   }

   // 良い例: 一括取得
   const usersWithPosts = await prisma.user.findMany({
     include: { posts: true },
   })
  1. インデックスの活用:
   model User {
     id    Int     @id
     email String  @unique
     name  String

     @@index([name]) // 検索頻度の高いフィールドにインデックス
   }

テストの書き方

Prisma操作のテスト例:

import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

describe('User CRUD', () => {
  beforeAll(async () => {
    await prisma.user.deleteMany()
  })

  test('create user', async () => {
    const user = await prisma.user.create({
      data: { name: 'Test User', email: 'test@example.com' },
    })
    expect(user.name).toBe('Test User')
  })

  afterAll(async () => {
    await prisma.$disconnect()
  })
})

プロダクション環境での注意点

  1. 接続プーリングの設定:
   DATABASE_URL="postgresql://user:password@localhost:5432/db?connection_limit=5"
  1. 長時間実行クエリの回避:
   // タイムアウト設定
   const controller = new AbortController()
   setTimeout(() => controller.abort(), 5000)

   await prisma.$queryRaw`SELECT pg_sleep(10)`, {
     signal: controller.signal,
   })
  1. ロギングの設定:
   const prisma = new PrismaClient({
     log: ['query', 'info', 'warn', 'error'],
   })

まとめ

このガイドでは、Next.jsアプリケーションでPrismaを使用してCRUD操作を行う方法を詳細に解説しました。Prismaの強力なクエリAPIを活用すれば、複雑なデータベース操作も簡単に実装できます。

次のステップとして、これらのCRUD操作を組み込んだREST APIの実装方法を学びましょう。PrismaとNext.js APIルートを組み合わせることで、完全なバックエンドシステムを構築できます。