PrismaのSchemaのAttributesを理解する
Prisma で Schema を書こうとすると、以下のように@id
や@default
、@unique
などの attributes の記述が必要になってきます。初見だと「これって必須なの?」「付けないとどうなるの?」と感じるかと思います。
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
author User? @relation(fields: [authorId], references: [id])
authorId Int?
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
}
この記事では、Prisma の attributes の中でも絶対に押さえるべき 7 つの基本 attributesと、その必要性を実際のケースと共に分かりやすく解説します。
ちなみに公式ドキュメントには以下にまとまっています。
Prisma の attributes は「フィールド用」と「モデル用」の 2 種類
まず最初に理解しておきたいのが、Prisma の attributes には 2 種類あることです。
@
1 つ)
フィールド attributes(フィールド(列)に対して個別に設定する属性です。
model User {
id Int @id @default(autoincrement()) // idフィールドに対する設定
email String @unique // emailフィールドに対する設定
name String // 設定なし
}
@@
2 つ)
ブロック attributes(モデル全体や複数フィールドの組み合わせに対して設定する属性です。
model User {
firstName String
lastName String
email String
@@unique([firstName, lastName]) // 複数フィールドの組み合わせに対する設定
@@index([email]) // モデル全体に対する設定
}
絶対に覚えるべき 7 つの基本 attributes
@id - プライマリキーの定義
何をするもの: レコードを一意に識別するための「番号札」を作ります。
model User {
id Int @id @default(autoincrement())
name String
}
付けないとどうなる?
// ❌ @idを付けない場合
model User {
name String
}
// 問題が発生:
// - どのレコードが誰なのか特定できない
// - findUnique()などの関数が使えない
// - 同じユーザーが重複する可能性
なぜ必須?: データベースでは各レコードを区別する仕組みが必要です。@id
がないと Prisma がどのレコードを操作すべきか判断できません。
@default - デフォルト値の自動設定
何をするもの: 値を指定しない場合に自動的に設定される値を定義します。
model Post {
id Int @id @default(autoincrement())
title String
published Boolean @default(false) // デフォルトは非公開
createdAt DateTime @default(now()) // 作成時刻を自動設定
views Int @default(0) // 閲覧数の初期値は0
}
付けないとどうなる?
// ❌ @defaultを付けない場合
await prisma.post.create({
data: {
title: "新しい記事",
published: false, // 毎回手動で指定が必要
createdAt: new Date(), // 毎回手動で指定が必要
views: 0 // 毎回手動で指定が必要
}
})
// ✅ @defaultがある場合
await prisma.post.create({
data: {
title: "新しい記事" // その他は自動設定
}
})
なぜ必要?: コードが簡潔になり、設定忘れを防げます。特に作成時刻や初期値は自動化すべきです。
@unique - 重複防止の制約
何をするもの: そのフィールドの値がテーブル全体で重複しないことを保証します。
model User {
id Int @id @default(autoincrement())
email String @unique // メールアドレスは重複不可
username String @unique // ユーザー名も重複不可
name String // 名前は重複OK
}
付けないとどうなる?
// ❌ @uniqueを付けない場合
await prisma.user.create({ data: { email: "test@example.com" } })
await prisma.user.create({ data: { email: "test@example.com" } }) // 成功してしまう!
// 結果として:
// - 同じメールに複数の通知が送られる
// - ログイン時にどのユーザーか特定できない
// - データの整合性が崩れる
なぜ必要?: システムが正常に動作するためには、メールアドレスやユーザー名などの識別子は重複してはいけません。
@@unique - 複数フィールドの組み合わせで重複防止
何をするもの: 複数フィールドの組み合わせが重複しないことを保証します。
model Employee {
id Int @id @default(autoincrement())
departmentId Int
employeeCode String
name String
@@unique([departmentId, employeeCode]) // 部署内で社員コードは重複不可
}
付けないとどうなる?
// 同じ部署に同じ社員コードが複数存在する状態が発生
await prisma.employee.create({
data: { departmentId: 1, employeeCode: "E001", name: "田中" }
})
await prisma.employee.create({
data: { departmentId: 1, employeeCode: "E001", name: "佐藤" } // 成功してしまう!
})
なぜ必要?: 部署ごとの社員コードや、学生の履修情報など、複数の条件の組み合わせで一意性を保つ必要がある場合に使用します。
@@id - 複合主キーの定義
何をするもの: 複数のフィールドを組み合わせてプライマリキーとします。
model CourseEnrollment {
studentId Int
courseId Int
grade String?
@@id([studentId, courseId]) // 学生IDとコースIDの組み合わせが主キー
}
なぜ使う?: 中間テーブルなどで、単一フィールドだけでは意味のある主キーが作れない場合に使用します。「学生 A が数学を履修」という情報は 1 回だけ登録できるようになります。
@relation - テーブル間の関連定義
何をするもの: テーブル同士の関係を定義し、関連データの取得を可能にします。
model Post {
id Int @id @default(autoincrement())
title String
author User @relation(fields: [authorId], references: [id])
authorId Int // 外部キー
}
model User {
id Int @id @default(autoincrement())
name String
posts Post[] // 1人のユーザーは複数の投稿を持てる
}
付けないとどうなる?
// ❌ @relationを付けない場合
// - author.nameのような関連データが取得できない
// - 存在しないユーザーIDを設定できてしまう
// ✅ @relationがある場合
const post = await prisma.post.findFirst({
include: { author: true } // 投稿者の情報も一緒に取得
})
console.log(post.author.name) // 投稿者の名前にアクセス可能
なぜ必要?: 関連データの取得やデータの整合性チェックに必須です。
@updatedAt - 更新時刻の自動記録
何をするもの: レコードが更新されるたびに、自動的に現在時刻を設定します。
model Post {
id Int @id @default(autoincrement())
title String
content String
createdAt DateTime @default(now()) // 作成時刻
updatedAt DateTime @updatedAt // 更新時刻(自動更新)
}
付けないとどうなる?
// ❌ @updatedAtを使わない場合
await prisma.post.update({
where: { id: 1 },
data: {
title: "更新されたタイトル",
updatedAt: new Date() // 毎回手動設定(忘れがち)
}
})
// ✅ @updatedAtがある場合
await prisma.post.update({
where: { id: 1 },
data: {
title: "更新されたタイトル" // updatedAtは自動更新
}
})
なぜ必要?: 更新履歴の追跡に必須で、手動での管理は忘れがちなので自動化すべきです。
パフォーマンス向上のための 3 つの attributes
@@index - 検索高速化のインデックス
よく検索するフィールドに設定すると、検索が劇的に速くなります。
model Post {
id Int @id @default(autoincrement())
title String
authorId Int
published Boolean
@@index([authorId]) // 著者での検索を高速化
@@index([authorId, published]) // 著者と公開状態での検索を高速化
}
インデックスがあれば検索パフォーマンスをよくすることができます。
@map/@map - 既存データベースとの互換性
既存のデータベースの命名規則に合わせる際に使用します。
model User {
firstName String @map("first_name") // DBでは first_name という列名
lastName String @map("last_name") // DBでは last_name という列名
@@map("users") // DBでは users というテーブル名
}
@ignore - セキュリティのためのフィールド除外
機密情報を Prisma Client から除外できます。
model User {
id Int @id
name String
hashedPassword String @ignore // Prisma Clientからアクセス不可
}
まとめ
Prisma の attributes は、データベースの構造と動作を定義する重要な要素です。
最低限これだけは押さえておきましょう
-
@id
- レコードの識別(必須) -
@default
- 自動値設定(効率化) -
@unique
- 重複防止(データの整合性) -
@relation
- テーブル間の関連(関連データの取得)
これらの attributes を適切に使用することで、堅牢で高速、かつ保守しやすいアプリケーションを構築できます。適切な場面で使い分けていきましょう!
Discussion