apps/docs/content/docs/orm/prisma-schema/data-model/relations/many-to-many-relations.mdx
Many-to-many (m-n) relations connect zero or more records on one side to zero or more on the other. They can be implicit (Prisma manages the relation table) or explicit (you define the relation table).
Use implicit m-n unless you need to store additional metadata in the relation table.
The relation table is represented as a model in the schema:
model Post {
id Int @id @default(autoincrement())
title String
categories CategoriesOnPosts[]
}
model Category {
id Int @id @default(autoincrement())
name String
posts CategoriesOnPosts[]
}
model CategoriesOnPosts {
post Post @relation(fields: [postId], references: [id])
postId Int
category Category @relation(fields: [categoryId], references: [id])
categoryId Int
assignedAt DateTime @default(now())
assignedBy String
@@id([postId, categoryId])
}
The relation table can store additional fields like assignedAt and assignedBy.
// Create post with new category
const post = await prisma.post.create({
data: {
title: "How to be Bob",
categories: {
create: [
{
assignedBy: "Bob",
category: { create: { name: "New category" } },
},
],
},
},
});
// Connect to existing categories
await prisma.post.create({
data: {
title: "My Post",
categories: {
create: [
{ assignedBy: "Bob", category: { connect: { id: 9 } } },
{ assignedBy: "Bob", category: { connect: { id: 22 } } },
],
},
},
});
// Query posts by category
const posts = await prisma.post.findMany({
where: { categories: { some: { category: { name: "New Category" } } } },
});
Prisma manages the relation table automatically:
model Post {
id Int @id @default(autoincrement())
title String
categories Category[]
}
model Category {
id Int @id @default(autoincrement())
name String
posts Post[]
}
// Create post with categories
const post = await prisma.post.create({
data: {
title: "How to become a butterfly",
categories: {
create: [{ name: "Magic" }, { name: "Butterflies" }],
},
},
});
// Get posts with categories
const posts = await prisma.post.findMany({
include: { categories: true },
});
@id (no composite IDs or @unique)@relation attribute needed (unless disambiguating)fields, references, onUpdate, or onDelete in @relationFor prisma db pull to recognize implicit m-n tables:
_CategoryToPost (underscore + model names alphabetically + To)A (FK to first model alphabetically) and B (FK to second)BUse @relation("MyRelationTable") on both sides to customize the table name.
MongoDB requires explicit ID arrays on both sides:
model Post {
id String @id @default(auto()) @map("_id") @db.ObjectId
categoryIDs String[] @db.ObjectId
categories Category[] @relation(fields: [categoryIDs], references: [id])
}
model Category {
id String @id @default(auto()) @map("_id") @db.ObjectId
name String
postIDs String[] @db.ObjectId
posts Post[] @relation(fields: [postIDs], references: [id])
}
// Find posts by category IDs
const posts = await prisma.post.findMany({
where: { categoryIDs: { hasSome: [id1, id2] } },
});
// Find posts by category name
const posts = await prisma.post.findMany({
where: { categories: { some: { name: { contains: "Servers" } } } },
});