設定と初期化
インストール
# Prismaと関連パッケージのインストール
npm install prisma @prisma/client
# または
yarn add prisma @prisma/client
初期化
# Prismaの初期化
npx prisma init
環境設定 (.env ファイル)
DATABASE_URL="postgresql://user:password@localhost:5432/mydatabase"
スキーマ定義 (schema.prisma)
基本設定
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql" // または "mysql", "sqlite", "sqlserver"
url = env("DATABASE_URL")
}
モデル定義
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
posts Post[]
profile Profile?
@@map("users") // テーブル名をカスタマイズ
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
tags Tag[]
@@index([authorId]) // インデックスの作成
}
model Profile {
id Int @id @default(autoincrement())
bio String?
user User @relation(fields: [userId], references: [id])
userId Int @unique
}
model Tag {
id Int @id @default(autoincrement())
name String @unique
posts Post[]
}
リレーションタイプ
// 1対1
model User {
id Int @id
profile Profile?
}
model Profile {
id Int @id
user User @relation(fields: [userId], references: [id])
userId Int @unique // @uniqueが必要
}
// 1対多
model User {
id Int @id
posts Post[]
}
model Post {
id Int @id
author User @relation(fields: [authorId], references: [id])
authorId Int
}
// 多対多
model Post {
id Int @id
tags Tag[]
}
model Tag {
id Int @id
posts Post[]
}
マイグレーション管理
マイグレーション作成
# 開発環境用
npx prisma migrate dev --name init
# 名前付きマイグレーション
npx prisma migrate dev --name add_user_profile
マイグレーション適用(本番環境用)
npx prisma migrate deploy
スキーマ更新(既存DBからスキーマを生成)
npx prisma db pull
DBスキーマをPrismaスキーマに合わせる(開発用)
npx prisma db push
マイグレーションのリセット
npx prisma migrate reset
Prisma Client
クライアント生成
npx prisma generate
基本的な使用法
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
async function main() {
// ここでPrisma操作を実行
}
main()
.catch(e => {
console.error(e)
process.exit(1)
})
.finally(async () => {
await prisma.$disconnect()
})
CRUD操作
作成(Create)
// 単一レコード作成
const user = await prisma.user.create({
data: {
name: '山田太郎',
email: 'taro@example.com',
},
})
// リレーションを含む作成
const userWithPosts = await prisma.user.create({
data: {
name: '佐藤花子',
email: 'hanako@example.com',
posts: {
create: [
{ title: '初めての投稿', content: '内容...' },
{ title: '2つ目の投稿', content: '内容...' }
],
},
},
include: {
posts: true, // 関連ポストも返す
},
})
// 複数レコード作成
const users = await prisma.user.createMany({
data: [
{ name: '鈴木一郎', email: 'ichiro@example.com' },
{ name: '田中二郎', email: 'jiro@example.com' },
],
})
読み取り(Read)
// 全件取得
const allUsers = await prisma.user.findMany()
// 条件付き取得
const user = await prisma.user.findUnique({
where: { id: 1 }
})
const userByEmail = await prisma.user.findUnique({
where: { email: 'taro@example.com' }
})
// 複合条件
const filteredPosts = await prisma.post.findMany({
where: {
AND: [
{ published: true },
{ title: { contains: 'Prisma' } }
],
},
})
// リレーションを含む取得
const usersWithPosts = await prisma.user.findMany({
include: {
posts: true,
},
})
// リレーションの条件付き取得
const usersWithPublishedPosts = await prisma.user.findMany({
include: {
posts: {
where: { published: true }
}
}
})
// ネストされたリレーション
const userWithPostsAndProfile = await prisma.user.findUnique({
where: { id: 1 },
include: {
posts: {
include: {
tags: true
}
},
profile: true
}
})
// 選択フィールド
const userNameOnly = await prisma.user.findUnique({
where: { id: 1 },
select: { name: true }
})
// ページネーション
const paginatedUsers = await prisma.user.findMany({
take: 10, // 10件取得
skip: 20, // 20件スキップ
orderBy: { createdAt: 'desc' }
})
// 集計
const userCount = await prisma.user.count()
const publishedPostCount = await prisma.post.count({
where: { published: true }
})
更新(Update)
// 単一レコード更新
const updatedUser = await prisma.user.update({
where: { id: 1 },
data: { name: '新しい名前' },
})
// 複数レコード更新
const publishAll = await prisma.post.updateMany({
where: { authorId: 1 },
data: { published: true },
})
// リレーションを含む更新
const userWithNewPost = await prisma.user.update({
where: { id: 1 },
data: {
posts: {
create: { title: '新しい投稿' }
}
},
include: { posts: true }
})
// upsert(存在すれば更新、なければ作成)
const upsertUser = await prisma.user.upsert({
where: { email: 'taro@example.com' },
update: { name: '山田太郎(更新)' },
create: { email: 'taro@example.com', name: '山田太郎(新規)' },
})
削除(Delete)
// 単一レコード削除
const deletedUser = await prisma.user.delete({
where: { id: 1 },
})
// 複数レコード削除
const deleteDrafts = await prisma.post.deleteMany({
where: { published: false },
})
// カスケード削除(リレーションを含む)
// スキーマで onDelete: Cascade を設定している場合
const deleteUserAndPosts = await prisma.user.delete({
where: { id: 1 },
})
高度なクエリ
トランザクション
const [newUser, newPost] = await prisma.$transaction([
prisma.user.create({
data: { name: '新ユーザー', email: 'new@example.com' }
}),
prisma.post.create({
data: { title: '新投稿', authorId: 1 }
})
])
// インタラクティブなトランザクション
const result = await prisma.$transaction(async (tx) => {
const user = await tx.user.findUnique({ where: { id: 1 } })
if (!user) throw new Error('ユーザーが見つかりません')
const post = await tx.post.create({
data: { title: 'タイトル', authorId: user.id }
})
return { user, post }
})
Raw SQL
// 直接SQLを実行
const result = await prisma.$queryRaw`SELECT * FROM users WHERE id = ${userId}`
// 型付きクエリ
const result = await prisma.$queryRaw<User[]>`SELECT * FROM users`
シード
シードスクリプト (prisma/seed.ts)
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
async function main() {
// データクリーンアップ
await prisma.post.deleteMany()
await prisma.user.deleteMany()
// データ作成
const user = await prisma.user.create({
data: {
name: 'テストユーザー',
email: 'test@example.com',
posts: {
create: [
{ title: 'テスト投稿1', published: true },
{ title: 'テスト投稿2', published: false },
]
}
}
})
console.log('シード完了', user)
}
main()
.catch(e => console.error(e))
.finally(async () => await prisma.$disconnect())
package.json 設定
{
"prisma": {
"seed": "ts-node prisma/seed.ts"
}
}
シード実行
npx prisma db seed
Prisma Studio(GUI)
npx prisma studio
ミドルウェア
const prisma = new PrismaClient()
// ロギングミドルウェア
prisma.$use(async (params, next) => {
console.log(`操作: ${params.model}.${params.action}`)
const before = Date.now()
const result = await next(params)
const after = Date.now()
console.log(`時間: ${after - before}ms`)
return result
})
デバッグとログ
const prisma = new PrismaClient({
log: ['query', 'info', 'warn', 'error'],
})
// または特定のレベルのみ
const prisma = new PrismaClient({
log: [
{ emit: 'stdout', level: 'query' },
{ emit: 'event', level: 'error' },
],
})
// イベントリスナー
prisma.$on('query', (e) => {
console.log('クエリ: ' + e.query)
console.log('パラメータ: ' + e.params)
console.log('実行時間: ' + e.duration + 'ms')
})
script
環境構築
# Dockerコンテナのビルドと起動
docker compose up -d --build
# Prisma Clientの生成
docker compose exec app npm run prisma:generate
prisma generate --schema ./src/prisma/schema.prisma
# 初期マイグレーションの実行
docker compose exec app npm run prisma:migrate-dev -- --name init
prisma migrate dev --schema ./src/prisma/schema.prisma
# シードデータの投入
docker compose exec app npm run prisma:seed
prisma db seed --schema ./src/prisma/schema.prisma
スキーマ変更関連
# 新しいマイグレーションの作成と実行(スキーマ変更後)
docker compose exec app npm run prisma:migrate-dev -- --name [変更内容を表す名前]
prisma migrate dev --schema ./src/prisma/schema.prisma
# 例: テーブル追加
docker compose exec app npm run prisma:migrate-dev -- --name add_categories_table
# 例: カラム追加
docker compose exec app npm run prisma:migrate-dev -- --name add_image_url_to_posts
シードデータ関連
# シードデータの投入
docker compose exec app npm run prisma:seed
prisma db seed --schema ./src/prisma/schema.prisma
# データベースをリセット(マイグレーションを再適用し、シードデータを投入)
docker compose exec app npx prisma migrate reset --force --schema ./src/prisma/schema.prisma
確認・デバッグ
# データの確認(Prisma Studio起動)
docker compose exec app npm run prisma:studio
prisma studio --schema ./src/prisma/schema.prisma --hostname 0.0.0.0
# PostgreSQLへのアクセス
npm run psql
docker exec -it prisma_docker_starter_db psql -U user -d mydatabase
# データベース構造の確認(PostgreSQLコンソール内)
\dt
\d sample_table