CodingNic

Associations and Relationships

Eager Loading with include and select

Associations and Relationships 15 min read

Eager Loading with include and select

Objectives

By the end of this lesson, you should be able to:

  • Load related data in the same query with include
  • Choose exactly which fields to return with select
  • Explain the “N+1 query” problem, and how include avoids it

💡 Why this matters: Modeling a relationship (Lessons 1 and 2) doesn’t automatically load it, by default, fetching a book returns just the book. This lesson covers pulling related data in efficiently, without a wall of extra queries.

⚠️ A note on verification: as throughout this module, the Prisma CLI can’t run inside this course’s own sandboxed tooling. The code below reflects Prisma Client’s stable, current, documented query API. Try it yourself, on your own machine, against a real database.

Without include

javascript
const book = await prisma.book.findUnique({ where: { id: 1 } });
console.log(book);
text
{ id: 1, title: 'Clean Code', authorId: 1 }

Just the book’s own fields, authorId is there, but the actual author isn’t loaded.

The N+1 Problem

A naive way to get each book’s author would be a separate query per book:

javascript
const books = await prisma.book.findMany();
for (const book of books) {
  const author = await prisma.author.findUnique({ where: { id: book.authorId } });
  console.log(book.title, author.name);
}

One query for the books, then one more query per book to fetch its author, ten books means eleven total queries. This is the “N+1 problem”, N extra queries for N results, and it gets slow fast.

Fixing It With include

javascript
const books = await prisma.book.findMany({
  include: { author: true }
});
console.log(books);
text
[
  { id: 1, title: 'Clean Code', authorId: 1, author: { id: 1, name: 'Robert Martin' } },
  { id: 2, title: 'Clean Architecture', authorId: 1, author: { id: 1, name: 'Robert Martin' } }
]

include: { author: true } loads the related author for every book in one round trip. Prisma generates a single efficient query underneath, no matter how many books come back, this replaces the entire N+1 loop above.

include Works Both Directions

javascript
const author = await prisma.author.findUnique({
  where: { id: 1 },
  include: { books: true }
});
console.log(author);
text
{ id: 1, name: 'Robert Martin', books: [ { id: 1, title: 'Clean Code', authorId: 1 }, { id: 2, title: 'Clean Architecture', authorId: 1 } ] }

And it works for many-to-many too:

javascript
const book = await prisma.book.findUnique({
  where: { id: 1 },
  include: { tags: true }
});
console.log(book);
text
{ id: 1, title: 'Clean Code', authorId: 1, tags: [ { id: 1, name: 'programming' } ] }

Choosing Fields With select

include adds relations on top of every field. select replaces the whole shape, returning only the fields explicitly listed:

javascript
const books = await prisma.book.findMany({
  select: {
    title: true,
    author: { select: { name: true } }
  }
});
console.log(books);
text
[
  { title: 'Clean Code', author: { name: 'Robert Martin' } },
  { title: 'Clean Architecture', author: { name: 'Robert Martin' } }
]

No id, no authorId, just the two fields actually asked for, nested the same way for the related author. select is the right choice when an API response (Module 10’s capstone) should only expose specific fields.

Try It

  1. Fetch a book with include: { author: true }, and confirm the author’s full object appears nested inside.
  2. Fetch an author with include: { books: true }, and confirm every one of their books appears in an array.
  3. Rewrite one of the queries above using select instead, returning only title and the author’s name.
  4. Explain, in your own words, what the N+1 problem is, and which single option fixes it.

Recap

  • By default, a Prisma query returns only a model’s own fields, related data needs include or select explicitly.
  • include: { relationName: true } loads related records in one efficient query, avoiding the N+1 problem of querying each relation separately.
  • select controls the exact shape of the response, including nested select for related models, useful whenever an API shouldn’t expose every field.

This is the final lesson of this module before exercises. Next module: the full CRUD query API, replacing the raw SQL from Modules 1 and 2 with Prisma Client.