▲14 ▼0 @iduarte 2026-08-24 prisma performance postgresql nodejs

Prisma N+1: the `include` you chose over `select`, and the query count that explains a 4-second endpoint

verbatim errorprisma:query SELECT * FROM "orders" WHERE "orders"."user_id" = $1 prisma:query SELECT * FROM "items" WHERE "items"."order_id" = $1 prisma:query SELECT * FROM "items" WHERE "items"."order_id" = $1 prisma:query SELECT * FROM "items" WHERE "items"."order_id" = $1 (... 487 more queries for one page render, 4.1s total)

Problem

An orders page that takes 4 seconds and, with log: [{ query: 'stdout' }], shows the same items query repeated once per order:

prisma:query SELECT * FROM "orders" WHERE "orders"."user_id" = $1 prisma:query SELECT * FROM "items" WHERE "items"."order_id" = $1 prisma:query SELECT * FROM "items" WHERE "items"."order_id" = $1 prisma:query SELECT * FROM "items" WHERE "items"."order_id" = $1 (... 487 more queries for one page render, 4.1s total)

Root cause

The loop pattern — fetch a list, then query per row — or its sneakier cousin: an include on the parent query that omits a nested level, followed by component code that lazily fetches the missing level per item:

// parent query includes orders but NOT their items const user = await prisma.user.findUnique({ where: { id: userId }, include: { orders: true }, // items missing on purpose "to keep it light" }); // template then fetches per order: for (const order of user.orders) { order.items = await prisma.item.findMany({ where: { orderId: order.id } }); // N+1 }

The trap is the reasoning in the comment: include: { orders: { include: { items: true } } } feels heavy because it returns everything, so people split it up "for performance" and create the N+1 they were avoiding. The real fix is one query shaped narrowly, not many queries.

fix preview — first 3 of 17 lines (js), truncated:
const user = await prisma.user.findUnique({ where: { id: userId }, select: { … 14 more lines in the fix

🔒 the fix — including 2 code blocks — is members-only. $1/mo unlocks everything.

🔒 comments and voting are for members. $1/mo · every diagnosis is free to read, plus 3 complete sample fixes.