Skip to content

Many-to-many relations

Source URL: https://docs.prisma.io/docs/orm/more/troubleshooting/many-to-many-relations

Learn how to model, query, and convert many-to-many relations with Prisma ORM

Modeling and querying many-to-many relations in relational databases can be challenging. This guide shows how to work with implicit and explicit many-to-many relations, and how to convert between them.

Implicit many-to-many relations let Prisma ORM handle the relation table internally:

model Post {
id Int @id @default(autoincrement())
title String
tags Tag[]
}
model Tag {
id Int @id @default(autoincrement())
name String @unique
posts Post[]
}
  • Creating records
await prisma.post.create({
data: {
title: "Types of relations",
tags: { create: [{ name: "dev" }, { name: "prisma" }] },
},
});
  • Querying with relations
await prisma.post.findMany({
include: { tags: true },
});

Result:

[
{
"id": 1,
"title": "Types of relations",
"tags": [
{ "id": 1, "name": "dev" },
{ "id": 2, "name": "prisma" }
]
}
]
  • Connecting and creating tags simultaneously
await prisma.post.update({
where: { id: 1 },
data: {
title: "Prisma is awesome!",
tags: { set: [{ id: 1 }, { id: 2 }], create: { name: "typescript" } },
},
});

Explicit relations are needed when you need to store extra fields in the relation table or when introspecting an existing database:

model Post {
id Int @id @default(autoincrement())
title String
tags PostTags[]
}
model PostTags {
id Int @id @default(autoincrement())
post Post? @relation(fields: [postId], references: [id])
tag Tag? @relation(fields: [tagId], references: [id])
postId Int?
tagId Int?
@@index([postId, tagId])
}
model Tag {
id Int @id @default(autoincrement())
name String @unique
posts PostTags[]
}
  • Creating records with explicit relations
await prisma.post.create({
data: {
title: "Types of relations",
tags: {
create: [{ tag: { create: { name: "dev" } } }, { tag: { create: { name: "prisma" } } }],
},
},
});
  • Querying with explicit relations
await prisma.post.findMany({
include: { tags: { include: { tag: true } } },
});
  • Mapping the response

To get a cleaner response similar to implicit relations:

const result = posts.map((post) => {
return { ...post, tags: post.tags.map((tag) => tag.tag) };
});

Sometimes you need to transition from implicit to explicit relations, for example to add metadata like timestamps to the relation.

  • Step 1: Add the explicit relation model

Keep the implicit relation while adding the new model:

model User {
id Int @id @default(autoincrement())
name String
posts Post[]
userPosts UserPost[]
}
model Post {
id Int @id @default(autoincrement())
title String
authors User[]
userPosts UserPost[]
}
model UserPost {
id Int @id @default(autoincrement())
userId Int
postId Int
user User @relation(fields: [userId], references: [id])
post Post @relation(fields: [postId], references: [id])
createdAt DateTime @default(now())
@@unique([userId, postId])
}

Run the migration:

npm

pnpm

yarn

bun

npx prisma migrate dev --name "added explicit relation"
  • Step 2: Migrate existing data
import { PrismaClient } from "../prisma/generated/client";
const prisma = new PrismaClient();
async function main() {
const users = await prisma.user.findMany({
include: { posts: true },
});
for (const user of users) {
for (const post of user.posts) {
await prisma.userPost.create({
data: {
userId: user.id,
postId: post.id,
},
});
}
}
console.log("Data migration completed.");
}
main()
.catch((e) => {
throw e;
})
.finally(async () => {
await prisma.$disconnect();
});
  • Step 3: Remove implicit relation columns

After migrating the data, remove the implicit relation columns:

model User {
id Int @id @default(autoincrement())
name String
userPosts UserPost[]
}
model Post {
id Int @id @default(autoincrement())
title String
userPosts UserPost[]
}

Run the migration:

npm

pnpm

yarn

bun

npx prisma migrate dev --name "removed implicit relation"

This will drop the implicit table _PostToUser.