Back to Articles
January 15, 2025 8 min

Optimizing Laravel Query Performance with Eager Loading

#Laravel
#Performance
#Tutorial

Optimizing Laravel Query Performance with Eager Loading

One of the most common performance issues in Laravel applications is the N+1 query problem. In this tutorial, I’ll show you how to identify and fix this problem using eager loading.

What is the N+1 Query Problem?

The N+1 query problem occurs when your code executes 1 query to retrieve N records, and then N additional queries to retrieve related data for each record.

Example: The Problem

// This looks innocent but causes N+1 queries!
$users = User::all(); // 1 query

foreach ($users as $user) {
    echo $user->posts->count(); // N queries (one per user)
}

If you have 100 users, this code executes 101 queries: 1 to get users + 100 to get each user’s posts.

Identifying N+1 Queries

Laravel provides the debugbar package to visualize queries:

composer require barryvdh/laravel-debugbar --dev

Look for repeated similar queries in the debugbar. That’s your N+1 problem!

Solution: Eager Loading

Use Laravel’s with() method to eager load relationships:

// Eager loading - only 2 queries!
$users = User::with('posts')->get(); // 2 queries total

foreach ($users as $user) {
    echo $user->posts->count(); // No additional queries
}

Now we only execute 2 queries: 1 for users + 1 for all posts.

Real-World Performance Benchmark

I tested this on a production dataset with 1,000 users:

MethodQueriesTime
Without Eager Loading1,0012,450ms
With Eager Loading258ms

Result: 97.6% faster! 🚀

Advanced: Nested Eager Loading

You can eager load nested relationships:

$users = User::with('posts.comments.author')->get();

This loads users → posts → comments → comment authors in just 4 queries!

Conditional Eager Loading

Load relationships only when needed:

$users = User::when($includeP osts, function ($query) {
    $query->with('posts');
})->get();

Lazy Eager Loading

If you forgot to eager load initially:

$users = User::all();

// Later in code, realize you need posts
$users->load('posts');

Common Pitfalls to Avoid

1. Over-Eager Loading

Don’t load relationships you don’t use:

// BAD: Loading data you don't need
User::with('posts', 'comments', 'likes', 'followers')->get();

2. Loading Too Much Data

Use select() to limit columns:

// Only get necessary columns
User::with(['posts' => function ($query) {
    $query->select('id', 'user_id', 'title');
}])->get();

3. Forgetting Count Queries

For counts, use withCount():

$users = User::withCount('posts')->get();

foreach ($users as $user) {
    echo $user->posts_count; // No additional queries!
}

Best Practices

  1. Always eager load when iterating over relationships
  2. Use Laravel Debugbar to monitor queries in development
  3. Profile production with tools like New Relic or Laravel Telescope
  4. Load only what you need - don’t over-fetch
  5. Use query caching for frequently accessed data

Conclusion

Eager loading is a simple technique that can dramatically improve your Laravel application’s performance. By being mindful of the N+1 problem and using with(), withCount(), and other eager loading methods, you can reduce database queries by 80-90% in many cases.

Remember: Always profile before and after to measure the actual impact!

Further Reading