
Next.jsでPrismaを使ったCRUD操作完全ガイド
2025-08-26Next.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)
この操作では:
user
テーブルに新しいレコードが追加されます- 作成されたレコードが返されます(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`)
}
}
パフォーマンス最適化のヒント
- 必要なフィールドのみ選択:
await prisma.user.findMany({
select: { id: true, name: true },
})
- バッチ処理の活用:
// 悪い例: 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 },
})
- インデックスの活用:
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()
})
})
プロダクション環境での注意点
- 接続プーリングの設定:
DATABASE_URL="postgresql://user:password@localhost:5432/db?connection_limit=5"
- 長時間実行クエリの回避:
// タイムアウト設定
const controller = new AbortController()
setTimeout(() => controller.abort(), 5000)
await prisma.$queryRaw`SELECT pg_sleep(10)`, {
signal: controller.signal,
})
- ロギングの設定:
const prisma = new PrismaClient({
log: ['query', 'info', 'warn', 'error'],
})
まとめ
このガイドでは、Next.jsアプリケーションでPrismaを使用してCRUD操作を行う方法を詳細に解説しました。Prismaの強力なクエリAPIを活用すれば、複雑なデータベース操作も簡単に実装できます。
次のステップとして、これらのCRUD操作を組み込んだREST APIの実装方法を学びましょう。PrismaとNext.js APIルートを組み合わせることで、完全なバックエンドシステムを構築できます。