N+1 queries: the ORM wrote them, not you
A list page that runs one query per row looks fine in development and falls over in production. Here's how to spot it from the database side.
With twenty rows of seed data, one extra query per row is invisible. With real data and real traffic, the same page sends hundreds of small queries per view, and the database spends its time on round trips rather than work.
// 1 query for the posts + 1 query per post for its author
const posts = await prisma.post.findMany({ take: 50 });
for (const post of posts) {
post.author = await prisma.user.findUnique({ where: { id: post.authorId } });
}
// Same result in a fixed number of queries, whatever the page size
const posts = await prisma.post.findMany({
take: 50,
include: { author: true },
});From the database side, it shows up as one tiny query with an enormous call count:
SELECT calls, round(mean_exec_time::numeric, 2) AS avg_ms, query
FROM pg_stat_statements
ORDER BY calls DESC
LIMIT 10;If a single-row lookup by primary key sits at the top with far more calls than the page gets views, you've found it.
This comes from our database performance work.