Back to all articles
Laravel Insights Jan 1, 2026 โˆ™ 1 min read

Boost Laravel App Speed with Flexible Caching

Learn how to leverage Laravel 12's Flexible Caching feature to optimize dashboard performance and enhance user experience.

An illustration showing a Laravel logo connected to a fast-forward icon and a database, symbolizing accelerated data retrieval through caching.

Boosting App Speed with Flexible Caching in Laravel

Learn how to leverage Laravel 12's Flexible Caching feature to optimize dashboard performance and enhance user experience.

In application development, speed is not just a feature; it's a cornerstone of a positive user experience. For data-intensive applications, especially those with complex dashboards, slow load times can frustrate users and hinder productivity. Caching is a fundamental strategy for mitigating this, but traditional caching methods often present a trade-off: serve fresh but slow data, or serve fast but potentially stale data. Laravel 12 introduces Flexible Caching, a powerful feature designed to offer the best of both worlds.

Flexible Caching allows your application to serve a cached response instantly while simultaneously refreshing the cache in the background if it's stale. This means users get an immediate experience, and the next request will receive the newly updated data. It eliminates the perceived latency of cache regeneration, providing a seamless and highly responsive interface even for complex, data-heavy queries.

This article, inspired by the "Laravel In Practice" series, explores how to implement Flexible Caching to dramatically improve application performance. We will cover the core concepts, provide a step-by-step implementation guide, and demonstrate its real-world impact on a dashboard's load times.

The Problem with Traditional Caching

Before diving into the solution, it's important to understand the limitations of standard caching techniques. A typical caching implementation in Laravel looks something like this:

$data = Cache::remember('dashboard.stats', now()->addHour(), function () {
    // This closure runs only when the cache is empty or expired.
    return DB::table('orders')->select(...)->get(); 
});

This Cache::remember approach is effective, but it has one significant drawback. When the cache expires, the next user to request the data must wait for the slow database query inside the closure to execute before receiving a response. For a query that takes several seconds, this results in a noticeable delay. The first user after an expiration pays a "performance tax." Flexible Caching was created to solve this exact problem.

Understanding Laravel's Flexible Caching

Introduced in Laravel 12, Cache::remember() was updated to accept a new stale_while_revalidating parameter. This feature allows the system to serve old, stale data immediately while a background process revalidates and updates the cache.

The new method signature looks like this:

Cache::remember(
    $key,
    $ttl,
    $callback,
    $stale_while_revalidating = null
);

When $stale_while_revalidating is set, Laravel checks if the cache has expired.

  • If the cache is fresh, it returns the cached data.
  • If the cache is stale (expired), it returns the old data and triggers the $callback to run in the background, updating the cache for subsequent requests.

This ensures that no user ever has to wait for a slow callback to complete.

Implementing Flexible Caching: A Step-by-Step Guide

Let's walk through a real-world example of optimizing a dashboard that displays several resource-intensive statistics. We'll refactor the code from using traditional caching to implementing Laravel's Flexible Caching feature.

Step 1: Identify Slow Queries

First, identify the parts of your application that are slow due to heavy database queries. A typical dashboard might have several widgets, each performing its own calculation.

Original (Unoptimized) Code:

// In a DashboardService or Controller

public function getStats()
{
    $totalUsers = User::count();
    $totalSales = Order::sum('amount');
    $pendingOrders = Order::where('status', 'pending')->count();
    // More slow queries...

    return compact('totalUsers', 'totalSales', 'pendingOrders');
}

Without caching, every page load triggers these queries, leading to poor performance.

Step 2: Create a Dedicated Caching Service

To keep the logic clean and reusable, encapsulate the caching logic within a dedicated service class. This adheres to the single-responsibility principle and makes the code easier to manage.

php artisan make:class Services/FlexibleCacheService

This service will act as a centralized wrapper around Laravel's Cache::remember function, making it easy to apply flexible caching across the application.

Step 3: Implement the Flexible Caching Logic

Inside FlexibleCacheService, create a method that applies the stale_while_revalidating logic.

// app/Services/FlexibleCacheService.php

namespace App\Services;

use Illuminate\Support\Facades\Cache;

class FlexibleCacheService
{
    public function remember(string $key, \Closure $callback, int $ttlInSeconds, int $staleInSeconds)
    {
        return Cache::remember(
            key: $key,
            ttl: $ttlInSeconds,
            callback: $callback,
            stale_while_revalidating: $staleInSeconds
        );
    }
}

Here, we've created a simple remember method that takes a key, a callback, a "time-to-live" (TTL) for the fresh cache, and a "stale" duration.

Step 4: Refactor the Dashboard to Use the Service

Now, modify the dashboard's data-fetching logic to use the new FlexibleCacheService. Instead of one large cache entry for all stats, it's often better to cache each statistic individually. This provides more granular control and prevents a single slow query from holding up the entire cache block.

// In a DashboardService or Controller

use App\Services\FlexibleCacheService;

protected $cacheService;

public function __construct(FlexibleCacheService $cacheService)
{
    $this->cacheService = $cacheService;
}

public function getTotalUsers()
{
    return $this->cacheService->remember(
        key: 'dashboard.stats.total_users',
        callback: fn () => User::count(),
        ttlInSeconds: 3600, // Cache for 1 hour
        staleInSeconds: 60 // Revalidate in background if older than 1 minute
    );
}

public function getTotalSales()
{
    return $this->cacheService->remember(
        key: 'dashboard.stats.total_sales',
        callback: fn () => Order::sum('amount'),
        ttlInSeconds: 3600,
        staleInSeconds: 60
    );
}

// ... other stat methods

With this implementation, when the total_users cache is older than one minute, the application will serve the stale count instantly and begin recounting the users in the background.

Performance Comparison: The Real Impact

The difference in performance is significant. Let's compare three scenarios for a dashboard page load:

  1. Original (No Caching): Every request directly queries the database. If queries take 3 seconds, every user waits 3 seconds.
  2. Optimized (Traditional Caching): Most requests are fast (e.g., 50ms). However, when the cache expires, one user waits the full 3 seconds for the cache to be rebuilt.
  3. Flexible Caching: Every request is fast. Even when the cache is stale, the response is instant. The cache regeneration happens invisibly in the background, ensuring no user experiences a delay.

The result is a consistently fast experience, which is the ideal outcome for any performance optimization effort.

Best Practices for Testing and Maintenance

  • Isolate Cache Keys: Use descriptive and unique cache keys to avoid collisions. A good practice is to include model names and specific identifiers (e.g., user.{$id}.profile).
  • Use Queues: For stale_while_revalidating to work without blocking, you must have a configured queue driver (like Redis or a database) and a running queue worker. The background revalidation is dispatched as a job to the queue.
  • Testing Your Cache Logic: When writing tests, you can use Laravel's Cache::fake() to assert that your caching logic is being called correctly without actually hitting the cache store.
  • Clearing Cache: Remember to clear relevant caches when underlying data changes. For example, if a new order is placed, you might want to invalidate the dashboard.stats.total_sales key to ensure the next request triggers a revalidation.

Conclusion

Laravel 12's Flexible Caching is a transformative feature for building high-performance applications. It solves a long-standing challenge in caching by eliminating the latency penalty associated with cache regeneration. By serving stale data instantly while revalidating in the background, you can deliver a consistently fast and fluid user experience, even on the most data-intensive pages.

By following the implementation steps outlined in this article, you can effectively integrate this powerful feature into your projects. Adopting Flexible Caching is a direct and efficient path to optimizing your application, enhancing user satisfaction, and building more resilient systems.


Related articles

Continue exploring Laravel insights and practical delivery strategies.

Laravel consulting

Need senior Laravel help for this topic?

Let's adapt these practices to your product and deliver the next milestone.