Start a Project

N+1 Queries in Laravel: How to Spot and Fix Them Fast

If your Laravel app starts slowing down as data grows, N+1 queries in Laravel are usually the first place to look.

It’s one of those bugs that’s completely invisible until it isn’t — and by then, your app is making 200 database calls for a single page load.

Let me show you what it is, how to catch it, and how to fix it.

What is the N+1 problem in Laravel?

Say you have a list of orders and you want to show the customer name for each one.

Looks fine, right? Here’s what actually happens:

  1. Laravel runs 1 query to fetch all orders
  2. Then for each order, it runs another query to fetch the customer

So if you have 100 orders, that’s 101 queries. 500 orders? 501 queries.

This is the N+1 problem — 1 query to get the list, then N more queries for the related data.

Your database gets hammered, your page slows down, and nothing in the code looks obviously wrong.

How to spot N+1 queries in Laravel

Option 1: Laravel Debugbar

Install the debugbar package:

Visit any page and you’ll see a toolbar at the bottom showing how many queries fired and how long each took.

If you see 50+ nearly identical queries, you’ve found your N+1.

Option 2: Laravel Telescope

Go to the Queries tab after a request. You’ll see duplicate queries stacking up — same table, slightly different IDs.

Check the official Laravel Telescope documentation for full setup instructions.

Option 3: DB::listen() for a quick check

No packages? Drop this in your AppServiceProvider::boot() temporarily:

Check your log file and count how many times the same query appears.

How to fix N+1 queries in Laravel with eager loading

The fix is almost always eager loading.

It tells Laravel to fetch the related data upfront in one query instead of one per record.

Change this:

To this:

Now Laravel runs just 2 queries total — fetch all orders, then fetch all related customers in one go.

One word — with() — and you go from 501 queries to 2.

Loading multiple relationships

You can eager load as many relationships as you need:

The dot notation handles nested relationships.

items.product means: load each order’s items, and for each item, load its product. Still just a handful of queries total.

Conditional eager loading with load()

Sometimes you don’t know upfront what you’ll need. Use load() after the fact.

For more on the difference between the two approaches, see our guide on lazy vs. eager loading in Laravel.

Same result — just more flexible when working with collections that come from elsewhere.

loadMissing() for partial collections

If some records might already have the relationship loaded and others don’t:

This only fires a query for records that don’t already have the relationship loaded. Useful inside loops or conditional logic.

When eager loading isn’t enough

Select only the columns you need:

This loads only id, name, and email from the customers table — not the full row.

Always include the foreign key (id here) or the relationship won’t link correctly.

Cache data that rarely changes:

If the same data hits the database on every request and barely changes, cache it.

For a deeper dive into database and caching performance, check out our guide on scaling Bagisto for large product catalogs.

It covers eager loading and caching strategies at scale.

Prevent N+1 queries in Laravel automatically

Laravel has a built-in way to throw an error whenever a lazy-loaded relationship fires unexpectedly.

Add this to AppServiceProvider::boot():

During development, any N+1 query throws an exception instead of silently running.

You’ll catch these before they ever reach production.

Quick recap

The fix takes 30 seconds once you know where to look. The tricky part is just noticing it exists — and now you know how.

Exit mobile version