Real applications don’t just have flat tables. A blog has users, posts, comments, tags, and likes. An e-commerce platform has products, categories, orders, and line items. This blog is about working with all of that complexity gracefully — using Prisma’s relational querying power with MySQL.
FETCHING
Fetching Related Data with `include`
`include` tells Prisma to JOIN and return related records. The type system knows exactly what shape the result will be.
`include` returns ALL fields of the related model. If you only need specific fields from related records, use nested `select` instead for smaller MySQL query payloads.
// Add an existing tag to a post (INSERT into _PostToTag join table)
await prisma.post.update({
where: { id: 1 },
data: {
tags: { connect: { id: 5 } },
},
});
// Connect multiple at once
await prisma.post.update({
where: { id: 1 },
data: {
tags: { connect: [{ id: 3 }, { id: 7 }, { id: 12 }] },
},
});
`disconnect` — Unlink Records
📘
// Remove tag #5 from this post (DELETE from join table)
await prisma.post.update({
where: { id: 1 },
data: {
tags: { disconnect: { id: 5 } },
},
`set` — Replace the Entire Relation Set
📘
// Replace ALL tags on this post with exactly [tag 3, tag 7]
await prisma.post.update({
where: { id: 1 },
data: {
tags: { set: [{ id: 3 }, { id: 7 }] },
},
});
MANY to MANY
Implicit vs Explicit Many-to-Many
Implicit (Prisma manages the MySQL join table)
📘
model Post {
id Int @id @default(autoincrement())
tags Tag[]
@@map("posts")
}
model Tag {
id Int @id @default(autoincrement())
name String @unique @db.VarChar(100)
posts Post[]
@@map("tags")
}
// Prisma creates "_PostToTag" join table in MySQL
Explicit (You control the join table)
Use explicit when you need metadata on the relation:
📘
model Post {
id Int @id @default(autoincrement())
tagRelations PostTag[]
@@map("posts")
}
model Tag {
id Int @id @default(autoincrement())
name String @unique @db.VarChar(100)
postRelations PostTag[]
@@map("tags")
}
model PostTag {
postId Int
tagId Int
addedBy String @db.VarChar(191) // Who added this tag
createdAt DateTime @default(now())
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade)
@@id([postId, tagId]) // Composite primary key
@@index([tagId]) // Index the FK in MySQL!
@@map("post_tags")
}
When to Use Which
Use Implicit when you only care that a relationship exists — e.g. tags on a post, roles on a user.
Use Explicit when the relationship itself carries data — e.g. when a user enrolled in a course, who approved a permission, what order items were added.
// Find users who have AT LEAST ONE published post (some)
const authors = await prisma.user.findMany({
where: {
posts: { some: { published: true } },
},
});
// Find users where ALL posts are published (every)
const prolificAuthors = await prisma.user.findMany({
where: { posts: { every: { published: true } } },
});
// Find users with NO posts (none)
const lurkers = await prisma.user.findMany({
where: { posts: { none: {} } },
});
// Filter with nested conditions
const activeAuthors = await prisma.user.findMany({
where: {
posts: {
some: {
AND: [{ published: true }, { views: { gt: 100 } }],
},
},
},
});
N + 1
And How Prisma Handles It
The N+1 Problem
The N+1 problem is one of the most common ORM performance bugs. It happens when you load N parent records and then fire 1 additional query per parent to load children — N+1 total MySQL queries.
widely attributed in software engineering communities
Thank You for Spending Your Valuable Time
I truly appreciate you taking the time to read blog. Your valuable time means a lot to me, and I hope you found the content insightful and engaging!
FAQ's
Frequently Asked Questions
No — you can't use both `include` and `select` at the root query level. They're mutually exclusive at the same level. However, within an `include`, you can use `select` to specify which fields of the relation to return.
Yes — self-referencing models work in MySQL. Define: `parent Category? @relation('sub', fields: [parentId], references: [id]); children Category[] @relation('sub')`. Add `@@index([parentId])` for the FK.
Yes — use `onDelete: Cascade` in your relation definition. Prisma generates the proper MySQL `ON DELETE CASCADE` constraint in the migration SQL. Always add `@@index([foreignKeyId])` on the child model for MySQL performance.
No hard limit, but deeply nested queries can generate complex MySQL JOINs. As a practical rule, avoid going beyond 3–4 levels of nesting. For deeper requirements, consider multiple queries or a GraphQL layer with DataLoader.
Mastering Relational Data with MySQL
Real applications don’t just have flat tables. A blog has users, posts, comments, tags, and likes. An e-commerce platform has products, categories, orders, and line items. This blog is about working with all of that complexity gracefully — using Prisma’s relational querying power with MySQL.
Fetching Related Data with `include`
Nested `select`
Combine `select` and `include` at any nesting level. prisma.io/docs/concepts/components/prisma-client/select-fields
Nested Writes
Prisma lets you create a parent and its children in a single atomic MySQL transaction.
`connect`, `disconnect`, `set` for Relations
`connect` — Link Existing Records
`disconnect` — Unlink Records
`set` — Replace the Entire Relation Set
Implicit vs Explicit Many-to-Many
Implicit (Prisma manages the MySQL join table)
Explicit (You control the join table)
When to Use Which
Relational Filters
Filter parent records based on their children’s properties. prisma.io/docs/reference/api-reference/prisma-client-reference#some
The N+1 Problem
The Problem (Anti-Pattern)
The Solution — Use `include`
Explore project snapshots or discuss custom web solutions.
Weeks of coding can save you hours of planning.
Thank You for Spending Your Valuable Time
I truly appreciate you taking the time to read blog. Your valuable time means a lot to me, and I hope you found the content insightful and engaging!
Frequently Asked Questions
No — you can't use both `include` and `select` at the root query level. They're mutually exclusive at the same level. However, within an `include`, you can use `select` to specify which fields of the relation to return.
Yes — self-referencing models work in MySQL. Define: `parent Category? @relation('sub', fields: [parentId], references: [id]); children Category[] @relation('sub')`. Add `@@index([parentId])` for the FK.
Yes — use `onDelete: Cascade` in your relation definition. Prisma generates the proper MySQL `ON DELETE CASCADE` constraint in the migration SQL. Always add `@@index([foreignKeyId])` on the child model for MySQL performance.
No hard limit, but deeply nested queries can generate complex MySQL JOINs. As a practical rule, avoid going beyond 3–4 levels of nesting. For deeper requirements, consider multiple queries or a GraphQL layer with DataLoader.
Related Blogs
Express Js
Prisma Relations & Nested Queries
Read MorePhilosophy of Programming
Flow State Destruction: Why Traditional “Deep Work” Is
Read MoreReact JS
Context Engineering: The New Cognitive Skill Dominating Engineering
Read More