If you’ve ever worked on a Laravel app that suddenly started slowing down, chances are your Eloquent queries might be the culprit. As your app scales and your data grows, even seemingly small inefficiencies in queries can cause performance bottlenecks.
In this post, we’ll walk through practical, real-world tips to write highly optimized Eloquent queries. Whether you’re building an API, a dashboard, or a SaaS platform, these practices will help you improve performance and make your app more scalable and responsive.
1. Understand and Eliminate the N+1 Query Problem
The N+1 query problem happens when your app executes one query for the main model and one additional query for each related model. For example, if you’re loading 100 posts with their authors, you could accidentally fire 101 queries!
Solution: Use Eager Loading
// Instead of this:
$posts = Post::all();
foreach ($posts as $post) {
echo $post->user->name;
}
// Do this:
$posts = Post::with('user')->get();
Why it matters: with()
tells Eloquent to fetch related models in a single query, drastically reducing query count and improving performance.
2. Catch N+1 Issues Early in Development
Sometimes itβs hard to spot N+1 problems, especially in large apps. Thankfully, thereβs a great package for this:
Tool:
beyondcode/laravel-query-detector
Install it with:
composer require --dev beyondcode/laravel-query-detector
It will alert you in the browser or log when it detects inefficient queries.
3. Use MySQL Indexing Wisely
MySQL (and MariaDB) use B-Tree indexes to speed up query lookups. If youβre querying large tables without indexes, your database is doing full table scans β ouch!
Index Best Practices
-
Add indexes to columns used in:
-
WHERE
clauses -
JOIN
conditions -
ORDER BY
statements
-
// Migration example
$table->index('email');
$table->index(['user_id', 'created_at']);
Check slow queries with:
SHOW INDEXES FROM your_table;
4. Avoid
SELECT *
β Be Specific
Fetching only what you need is both faster and more memory-efficient.
// Avoid this:
$users = User::all();
// Better:
$users = User::select('id', 'name', 'email')->get();
Especially important in APIs where payload size matters.
5. Chunking for Large Datasets
When working with thousands (or millions) of records, donβt try to load them all into memory.
Use
chunk()
User::chunk(100, function ($users) {
foreach ($users as $user) {
// Process each user
}
});
This keeps memory usage low and avoids timeouts.
6. Use
chunkById()
for Changing Data
If your data is updated or deleted during processing, chunkById()
is a safer choice:
User::chunkById(100, function ($users) {
foreach ($users as $user) {
// Safe for large or dynamic datasets
}
});
It uses the primary key to paginate reliably, avoiding duplicate or missing records.
7. Optimize Pagination with βSeek Methodβ
Typical pagination using LIMIT
and OFFSET
becomes slower as the offset increases. Instead, use “seek method” pagination:
$lastId = 100;
$posts = Post::where('id', '>', $lastId)
->orderBy('id')
->limit(20)
->get();
This is faster and more efficient, especially for infinite scroll APIs or mobile apps.