How to find and fix N+1 queries
An N+1 query is one query to load a list, then one more query for each item in it. With ten products in your test database the page feels instant. With ten thousand in production it sends ten thousand and one queries, and the page times out on the day you get traffic. Reviews miss it because the loop and the query usually sit in different places: the loop in a component, the query inside a helper.
What it looks like
const orders = await prisma.order.findMany({ where: { shopId } });
for (const order of orders) {
order.customer = await prisma.customer.findUnique({ where: { id: order.customerId } });
}const orders = await prisma.order.findMany({
where: { shopId },
include: { customer: true },
});When there is no relation to include, collect the IDs and load them in one query with where: { id: { in: ids } }, then match them up in memory.
The same fix in other frameworks
# ForeignKey / OneToOne: a join
orders = Order.objects.filter(shop=shop).select_related("customer")
# ManyToMany / reverse ForeignKey: one extra query for all rows
orders = Order.objects.filter(shop=shop).prefetch_related("items")orders = Order.where(shop: shop).includes(:customer, :items)$orders = Order::where('shop_id', $shopId)->with(['customer', 'items'])->get();select o.*, c.name as customer_name
from orders o
join customers c on c.id = o.customer_id
where o.shop_id = $1;Catch it with a test
log: ['query'] in Prisma, django.db.backends at DEBUG, the Rails log, or DB::listen in Laravel) and load the page. If the same query repeats with only the ID changing, you have an N+1.assertNumQueries; in Rails the bullet gem raises on N+1 in tests. The count must stay the same with 5 rows and with 50.How Zelsis catches this
Zelsis flags database calls made inside a loop over query results in each pull request, so the slow page is caught before it merges instead of on the day you get traffic.
- DB-PERF-01ORM query inside a loop (N+1)
Check your own repo
Public repositories scan free. Paste a GitHub URL on the dashboard and every finding comes with the file, the line and a fix.
Scan a repository free