リレーション
リレーションとは、Prismaスキーマにおける2つのモデル間の接続です。例えば、1人のユーザーが複数のブログ投稿を持つことができるため、UserとPostの間には1対多のリレーションが存在します。
以下のPrismaスキーマは、UserモデルとPostモデルの間の1対多リレーションを定義しています。リレーションの定義に関わるフィールドが強調表示されています。
- リレーショナルデータベース
- MongoDB
model User {
id Int @id @default(autoincrement())
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
author User @relation(fields: [authorId], references: [id])
authorId Int // relation scalar field (used in the `@relation` attribute above)
title String
}
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
posts Post[]
}
model Post {
id String @id @default(auto()) @map("_id") @db.ObjectId
author User @relation(fields: [authorId], references: [id])
authorId String @db.ObjectId // relation scalar field (used in the `@relation` attribute above)
title String
}
Prisma ORMレベルでは、User / Postリレーションは以下で構成されます。
- 2つのリレーションフィールド:
authorとposts。リレーションフィールドはPrisma ORMレベルでモデル間の接続を定義し、データベースには存在しません。これらのフィールドはPrisma Clientを生成するために使用されます。 @relation属性によって参照されるスカラフィールドauthorId。このフィールドはデータベースに存在します。これはPostとUserを接続する外部キーです。
Prisma ORMレベルでは、2つのモデル間の接続は、リレーションの両側にあるリレーションフィールドによって常に表されます。
データベースにおけるリレーション
リレーショナルデータベース
以下のエンティティ関係図は、リレーショナルデータベースにおけるUserテーブルとPostテーブルの間の同じ1対多リレーションを定義しています。

SQLでは、2つのテーブル間のリレーションを作成するために外部キーを使用します。外部キーはリレーションの片側に保存されます。この例は以下で構成されます。
PostテーブルにあるauthorIdという名前の外部キーカラム。Userテーブルにあるidという名前の主キーカラム。PostテーブルのauthorIdカラムは、Userテーブルのidカラムを参照しています。
Prismaスキーマでは、外部キー/主キーの関係はauthorフィールドの@relation属性によって表現されます。
author User @relation(fields: [authorId], references: [id])
注: Prismaスキーマのリレーションは、データベースのテーブル間に存在する関係を表します。データベースにその関係が存在しない場合、Prismaスキーマにも存在しません。
MongoDB
MongoDBの場合、Prisma ORMは現在、正規化されたデータモデル設計を使用しており、これはリレーショナルデータベースと同様に、ドキュメントがIDによって互いを参照することを意味します。
以下のドキュメントはUser(Userコレクション内)を表します。
{ "_id": { "$oid": "60d5922d00581b8f0062e3a8" }, "name": "Ella" }
以下のPostドキュメントのリスト(Postコレクション内)は、それぞれ同じユーザーを参照するauthorIdフィールドを持っています。
[
{
"_id": { "$oid": "60d5922e00581b8f0062e3a9" },
"title": "How to make sushi",
"authorId": { "$oid": "60d5922d00581b8f0062e3a8" }
},
{
"_id": { "$oid": "60d5922e00581b8f0062e3aa" },
"title": "How to re-install Windows",
"authorId": { "$oid": "60d5922d00581b8f0062e3a8" }
}
]
このデータ構造は、複数のPostドキュメントが同じUserドキュメントを参照するため、1対多リレーションを表します。
IDとリレーションスカラフィールドに対する@db.ObjectId
モデルのIDがObjectId(Stringフィールドで表される)の場合、モデルのIDとリレーションの反対側のリレーションスカラフィールドに@db.ObjectIdを追加する必要があります。
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
posts Post[]
}
model Post {
id String @id @default(auto()) @map("_id") @db.ObjectId
author User @relation(fields: [authorId], references: [id])
authorId String @db.ObjectId // relation scalar field (used in the `@relation` attribute above)
title String
}
Prisma Clientにおけるリレーション
Prisma ClientはPrismaスキーマから生成されます。以下の例は、Prisma Clientを使用してレコードの取得、作成、更新を行う際にリレーションがどのように現れるかを示しています。
レコードとネストされたレコードの作成
以下のクエリは、1つのUserレコードと2つの関連するPostレコードを作成します。
const userAndPosts = await prisma.user.create({
data: {
posts: {
create: [
{ title: 'Prisma Day 2020' }, // Populates authorId with user's id
{ title: 'How to write a Prisma schema' }, // Populates authorId with user's id
],
},
},
})
基盤となるデータベースでは、このクエリは以下のようになります。
- 自動生成された
id(例:20)を持つUserを作成します。 - 2つの新しい
Postレコードを作成し、両方のレコードのauthorIdを20に設定します。
レコードを取得し、関連レコードを含める
以下のクエリは、idでUserを取得し、関連するPostレコードを含めます。
const getAuthor = await prisma.user.findUnique({
where: {
id: "20",
},
include: {
posts: true, // All posts where authorId == 20
},
});
基盤となるデータベースでは、このクエリは以下のようになります。
idが20のUserレコードを取得します。authorIdが20のすべてのPostレコードを取得します。
既存のレコードを別の既存のレコードに関連付ける
以下のクエリは、既存のPostレコードを既存のUserレコードに関連付けます。
const updateAuthor = await prisma.user.update({
where: {
id: 20,
},
data: {
posts: {
connect: {
id: 4,
},
},
},
})
基盤となるデータベースでは、このクエリはネストされたconnectクエリを使用して、idが4の投稿をidが20のユーザーにリンクします。クエリは以下の手順でこれを行います。
- クエリはまず、
idが20のユーザーを検索します。 - 次にクエリは
authorID外部キーを20に設定します。これにより、idが4の投稿がidが20のユーザーにリンクされます。
このクエリでは、authorIDの現在の値は関係ありません。クエリは、現在の値にかかわらず、authorIDを20に変更します。
リレーションの種類
Prisma ORMには3つの異なるタイプ(またはカーディナリティ)のリレーションがあります。
以下のPrismaスキーマには、すべてのタイプのリレーションが含まれています。
- 1対1:
User↔Profile - 1対多:
User↔Post - 多対多:
Post↔Category
- リレーショナルデータベース
- MongoDB
model User {
id Int @id @default(autoincrement())
posts Post[]
profile Profile?
}
model Profile {
id Int @id @default(autoincrement())
user User @relation(fields: [userId], references: [id])
userId Int @unique // relation scalar field (used in the `@relation` attribute above)
}
model Post {
id Int @id @default(autoincrement())
author User @relation(fields: [authorId], references: [id])
authorId Int // relation scalar field (used in the `@relation` attribute above)
categories Category[]
}
model Category {
id Int @id @default(autoincrement())
posts Post[]
}
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
posts Post[]
profile Profile?
}
model Profile {
id String @id @default(auto()) @map("_id") @db.ObjectId
user User @relation(fields: [userId], references: [id])
userId String @unique @db.ObjectId // relation scalar field (used in the `@relation` attribute above)
}
model Post {
id String @id @default(auto()) @map("_id") @db.ObjectId
author User @relation(fields: [authorId], references: [id])
authorId String @db.ObjectId // relation scalar field (used in the `@relation` attribute above)
categories Category[] @relation(fields: [categoryIds], references: [id])
categoryIds String[] @db.ObjectId
}
model Category {
id String @id @default(auto()) @map("_id") @db.ObjectId
posts Post[] @relation(fields: [postIds], references: [id])
postIds String[] @db.ObjectId
}
このスキーマはデータモデルの例と同じですが、スカラフィールド(必須のリレーションスカラフィールドを除く)がすべて削除されており、リレーションフィールドに焦点を当てることができます。
この例では暗黙の多対多リレーションを使用しています。これらのリレーションは、リレーションの曖昧さを解消する必要がある場合を除き、@relation属性を必要としません。
リレーショナルデータベースとMongoDBでは、特に多対多リレーションの構文が若干異なることに注意してください。
リレーショナルデータベースの場合、以下のエンティティ関係図は、サンプルPrismaスキーマに対応するデータベースを表します。

MongoDBの場合、Prisma ORMは正規化されたデータモデル設計を使用しており、これはリレーショナルデータベースと同様に、ドキュメントがIDによって互いを参照することを意味します。MongoDBセクションで詳細を確認してください。
暗黙的および明示的な多対多リレーション
リレーショナルデータベースにおける多対多リレーションは、2つの方法でモデル化できます。
- 明示的な多対多リレーション。この場合、リレーションテーブルはPrismaスキーマに明示的なモデルとして表現されます。
- 暗黙的な多対多リレーション。この場合、Prisma ORMがリレーションテーブルを管理し、Prismaスキーマには表示されません。
暗黙的な多対多リレーションでは、両方のモデルが単一の@idを持つ必要があります。以下の点に注意してください。
- 複数フィールドのIDを使用することはできません。
@idの代わりに@uniqueを使用することはできません。
これらの機能のいずれかを使用するには、代わりに明示的な多対多を設定する必要があります。
暗黙的な多対多リレーションは、基盤となるデータベースではリレーションテーブルとして依然として現れます。ただし、Prisma ORMがこのリレーションテーブルを管理します。
明示的な多対多リレーションの代わりに暗黙的な多対多リレーションを使用すると、Prisma Client APIがよりシンプルになります(例えば、ネストされた書き込みのネストレベルが1つ減るためです)。
Prisma Migrateを使用せず、イントロスペクションからデータモデルを取得する場合でも、Prisma ORMのリレーションテーブルの規則に従うことで、暗黙的な多対多リレーションを利用できます。
リレーションフィールド
リレーションフィールドは、Prisma モデル上のフィールドであり、スカラ型を持ちません。代わりに、その型は別のモデルです。
すべてのリレーションには、各モデルに1つずつ、厳密に2つのリレーションフィールドが必要です。1対1および1対多リレーションの場合、@relation属性の2つのリレーションフィールドのいずれかによってリンクされる追加のリレーションスカラフィールドが必要です。このリレーションスカラフィールドは、基盤となるデータベースにおける外部キーを直接表現するものです。
- リレーショナルデータベース
- MongoDB
model User {
id Int @id @default(autoincrement())
email String @unique
role Role @default(USER)
posts Post[] // relation field (defined only at the Prisma ORM level)
}
model Post {
id Int @id @default(autoincrement())
title String
author User @relation(fields: [authorId], references: [id]) // relation field (uses the relation scalar field `authorId` below)
authorId Int // relation scalar field (used in the `@relation` attribute above)
}
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
email String @unique
role Role @default(USER)
posts Post[] // relation field (defined only at the Prisma ORM level)
}
model Post {
id String @id @default(auto()) @map("_id") @db.ObjectId
title String
author User @relation(fields: [authorId], references: [id]) // relation field (uses the relation scalar field `authorId` below)
authorId String @db.ObjectId // relation scalar field (used in the `@relation` attribute above)
}
postsとauthorはどちらもリレーションフィールドです。なぜなら、それらの型はスカラ型ではなく、他のモデルだからです。
また、アノテーション付きリレーションフィールドであるauthorが、@relation属性内でPostモデル上のリレーションスカラフィールドauthorIdをリンクする必要があることにも注意してください。リレーションスカラフィールドは、基盤となるデータベースにおける外部キーを表します。
両方のリレーションフィールド(すなわち、postsとauthor)は純粋にPrisma ORMレベルで定義されており、データベースには現れません。
アノテーション付きリレーションフィールド
リレーションの片側が@relation属性でアノテーションされる必要があるリレーションは、アノテーション付きリレーションフィールドと呼ばれます。これには以下が含まれます。
- 1対1リレーション
- 1対多リレーション
- MongoDBのみの多対多リレーション
@relation属性でアノテーションされたリレーション側は、基盤となるデータベースに外部キーを保存する側を表します。外部キーを表す「実際の」フィールドもそのリレーション側に必要であり、それはリレーションスカラフィールドと呼ばれ、@relation属性内で参照されます。
- リレーショナルデータベース
- MongoDB
author User @relation(fields: [authorId], references: [id])
authorId Int
author User @relation(fields: [authorId], references: [id])
authorId String @db.ObjectId
スカラフィールドは、@relation属性のfieldsで使用されると、リレーションスカラフィールドになります。
リレーションスカラフィールド
リレーションスカラフィールドの命名規則
リレーションスカラフィールドは常にリレーションフィールドに属するため、以下の命名規則が一般的です。
- リレーションフィールド:
author - リレーションスカラフィールド:
authorId(リレーションフィールド名 +Id)
@relation属性
@relation属性は、リレーションフィールドにのみ適用でき、スカラフィールドには適用できません。
@relation属性は以下の場合に必要です。
- 1対1または1対多リレーションを定義する場合、リレーションの片側に(対応するリレーションスカラフィールドとともに)必要です。
- リレーションの曖昧さを解消する必要がある場合(例えば、同じモデル間に2つのリレーションがある場合など)。
- 自己リレーションを定義する場合。
- MongoDBの多対多リレーションを定義する場合。
- 基盤となるデータベースでリレーションテーブルがどのように表現されるかを制御する必要がある場合(例: リレーションテーブルに特定の名前を使用する)。
注: リレーショナルデータベースにおける暗黙の多対多リレーションは、
@relation属性を必要としません。
リレーションの曖昧さ解消
同じ2つのモデル間に2つのリレーションを定義する場合、それらの曖昧さを解消するために@relation属性にname引数を追加する必要があります。なぜそれが必要なのかの例として、以下のモデルを考えてみましょう。
- リレーショナルデータベース
- MongoDB
// NOTE: This schema is intentionally incorrect. See below for a working solution.
model User {
id Int @id @default(autoincrement())
name String?
writtenPosts Post[]
pinnedPost Post?
}
model Post {
id Int @id @default(autoincrement())
title String?
author User @relation(fields: [authorId], references: [id])
authorId Int
pinnedBy User? @relation(fields: [pinnedById], references: [id])
pinnedById Int?
}
// NOTE: This schema is intentionally incorrect. See below for a working solution.
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
name String?
writtenPosts Post[]
pinnedPost Post?
}
model Post {
id String @id @default(auto()) @map("_id") @db.ObjectId
title String?
author User @relation(fields: [authorId], references: [id])
authorId String @db.ObjectId
pinnedBy User? @relation(fields: [pinnedById], references: [id])
pinnedById String? @db.ObjectId
}
その場合、リレーションは曖昧であり、解釈には4つの異なる方法があります。
User.writtenPosts↔Post.author+Post.authorIdUser.writtenPosts↔Post.pinnedBy+Post.pinnedByIdUser.pinnedPost↔Post.author+Post.authorIdUser.pinnedPost↔Post.pinnedBy+Post.pinnedById
これらのリレーションの曖昧さを解消するには、リレーションフィールドに@relation属性をアノテーションし、name引数を指定する必要があります。任意のname(空の文字列""を除く)を設定できますが、リレーションの両側で同じである必要があります。
- リレーショナルデータベース
- MongoDB
model User {
id Int @id @default(autoincrement())
name String?
writtenPosts Post[] @relation("WrittenPosts")
pinnedPost Post? @relation("PinnedPost")
}
model Post {
id Int @id @default(autoincrement())
title String?
author User @relation("WrittenPosts", fields: [authorId], references: [id])
authorId Int
pinnedBy User? @relation("PinnedPost", fields: [pinnedById], references: [id])
pinnedById Int? @unique
}
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
name String?
writtenPosts Post[] @relation("WrittenPosts")
pinnedPost Post? @relation("PinnedPost")
}
model Post {
id String @id @default(auto()) @map("_id") @db.ObjectId
title String?
author User @relation("WrittenPosts", fields: [authorId], references: [id])
authorId String @db.ObjectId
pinnedBy User? @relation("PinnedPost", fields: [pinnedById], references: [id])
pinnedById String? @unique @db.ObjectId
}