DEV Community

Cover image for Laravel Middleware Magic: Use Cases You Didn’t Know About
ZoZo
ZoZo

Posted on

Laravel Middleware Magic: Use Cases You Didn’t Know About

You probably know middleware as the gatekeeper for authentication and authorization. But middleware can do so much more than just check if a user is logged in. In real-world projects, it’s one of your most powerful tools for handling cross-cutting concerns and improving performance. It often works quietly behind the scenes, streamlining your code and reducing duplication.

In this article, I’ll show you how to unlock the full potential of Laravel middleware with practical examples you can start using right away—even if you prefer minimal JavaScript or are building complex user features like wishlists and profile management.

kirby

Real-Life Middleware Use Cases

1. Dynamic User Preferences Without Extra Queries

Let’s say your app allows users to customize their profiles and manage wishlists. Instead of manually fetching user preferences in every controller, you can use middleware to inject those preferences into all views or the request itself. This eliminates repetitive logic and makes your controllers cleaner.

Here’s a quick example:

public function handle($request, Closure $next)
{
    if (auth()->check()) {
      $preferences = auth()->user()->preferences ?? [];
      view()->share('preferences', $preferences);
    }
    return $next($request);
}
Enter fullscreen mode Exit fullscreen mode

You can also inject these preferences directly into the request object if needed:

$request->attributes->set('preferences', $preferences);

2. API Rate Limiting Per User Role

Laravel’s built-in throttling is helpful, but sometimes you need more control. For example, what if admins, premium users, and guests should have different rate limits?

public function handle($request, Closure $next)
{
    $user = $request->user();
    $limit = match (true) {
        !$user => 10,
        $user->isAdmin() => 100,
        $user->isPremium() => 60,
        default => 30,
    };

    $key = 'rate_limit:' . ($user?->id ?? $request->ip());

    if (RateLimiter::tooManyAttempts($key, $limit)) {
        abort(429, 'Too many requests.');
    }

    RateLimiter::hit($key, 60); // 60-second window

    return $next($request);
}
Enter fullscreen mode Exit fullscreen mode

You can write custom middleware that checks the user’s role and applies the appropriate rate limit dynamically. This keeps your API fair and responsive without bloating controller logic.

night

3. Feature Flags and A/B Testing

Middleware can manage feature flags by checking the user’s session or profile and enabling or disabling routes or views accordingly.

public function handle($request, Closure $next)
{
    if (auth()->check() && !auth()->user()->isInTestGroup('wishlist_redesign')) {
        return redirect()->route('dashboard');
    }

    return $next($request);
}
Enter fullscreen mode Exit fullscreen mode

For instance, you can show a redesigned wishlist UI to only 10% of users, or roll out beta features gradually.

4. On-the-Fly Content Localization

If your app supports multiple languages, you don’t need heavy front-end logic to handle localization. Middleware can detect a user’s preferred language—based on profile settings, cookies, or browser headers—and set the app locale before any views or responses are rendered.

public function handle($request, Closure $next)
{
    $locale = auth()->user()?->locale ?? $request->getPreferredLanguage(['en', 'hu', 'de']);
    app()->setLocale($locale);

    return $next($request);
}
Enter fullscreen mode Exit fullscreen mode

This way, even error messages and validation responses appear in the correct language, and your app feels native to the user.

php

5. Request Data Sanitization and Transformation

Rather than manually sanitizing form input in every controller, you can create middleware that transforms or cleans up request data globally.

public function handle($request, Closure $next)
{
    if ($request->has('phone')) {
        $normalized = preg_replace('/\D/', '', $request->input('phone'));
        $request->merge(['phone' => $normalized]);
    }

    return $next($request);
}
Enter fullscreen mode Exit fullscreen mode

Normalize phone numbers, trim strings, or cast values to the correct type before they hit your controllers. This is especially useful for APIs or shared form endpoints.

Lesser-Known Middleware Tricks

Early Response Short-Circuiting

Middleware can return a response before the application logic runs. This is useful for use cases like:

  • IP whitelisting

  • maintenance mode

  • serving cached responses

It’s a great way to save processing time and system resources when certain conditions are met.

public function handle($request, Closure $next)
{
    if (Cache::has('products_list') && $request->is('api/products')) {
        return response()->json(Cache::get('products_list'));
    }

    return $next($request);
}
Enter fullscreen mode Exit fullscreen mode

white

Chaining Middleware for Complex Logic

Break logic into layers. For example:

  • one middleware checks if the user is authenticated

  • another ensures their profile is complete

public function handle($request, Closure $next)
{
    if (!auth()->user()->hasCompletedProfile()) {
        return redirect()->route('profile.edit')->with('error', 'Please complete your profile.');
    }

    return $next($request);
}
Enter fullscreen mode Exit fullscreen mode
  • a third verifies if they’ve accepted the latest terms

This keeps responsibilities separate, your code modular, and testing much easier.

Injecting Data Into the Request Object

It can attach things like wishlist status or profile completion directly to the request object. This makes it easy to write clean, readable controllers.

$request->attributes->set('wishlist', auth()->user()?->wishlist);

Using Middleware Groups for Clean Routing

If you have routes that share the same logic—like authentication, email verification, and profile checks—bundle the middleware into a group and apply it cleanly to those routes.

Route::middleware(['auth', 'verified', 'profile.complete'])->group(function () {
    Route::get('/wishlist', [WishlistController::class, 'index']);
});
Enter fullscreen mode Exit fullscreen mode

pomi

Pro Tips for Real Projects

  • Middleware is perfect for anything that should run globally or for a group of routes. Don’t let your controllers become dumping grounds for repetitive checks.

  • Use it to enforce business rules—for example, only users with a complete profile can access certain features.

  • Keep middleware responsibilities single-purpose. That makes them easier to test, understand, and reuse.

  • Don’t forget global middleware for handling things like security headers, logging, or CORS.

Bonus Use Case: IP-Based Access Control

Want to restrict sensitive parts of your app to certain IP addresses (e.g. admin panel)?

public function handle($request, Closure $next)
{
    $allowedIps = ['123.45.67.89', '98.76.54.32'];

    if (!in_array($request->ip(), $allowedIps)) {
        abort(403, 'Access denied.');
    }

    return $next($request);
}
Enter fullscreen mode Exit fullscreen mode

This is a simple but powerful security layer that runs before your app loads.

kid

Conclusion

Whether you’re managing user preferences, improving API responsiveness, or running feature experiments, it’s your behind-the-scenes ally for cleaner, smarter apps. The more you use it strategically, the more streamlined your codebase becomes—and the easier it is to scale and maintain.

Top comments (7)

Collapse
 
xwero profile image
david duymelinck

While you present good cases, there are also a few cases I think don't belong in middleware.

The user preferences. The reason why I don't think it belongs in middleware is because not every preference is affecting every route. I would take a more fine grained approach.

Data sanitation. I would do this in a controller, FormRequest or domain, not in middleware. For the global case my pick would be in a domain class.

Middleware for me is like air traffic control. The destination is planned and most of the times it goes as planned. But in certain circumstances other measures are needed.

Collapse
 
cyber_aurora_ profile image
ZoZo

Thanks so much for sharing your thoughts. Totally agree that middleware shouldn’t be treated as a one-size-fits-all solution. Not every route needs access to every preference. I was thinking more along the lines of truly global settings (like UI themes), but I should’ve clarified that route-specific preferences definitely belong in controllers or dedicated services. Middleware only makes sense when those preferences are needed on like 90%+ of routes. Really loved your suggeston about using FormRequests and domain classes for data sanitation, that’s actually my go-to approach as well for entity-specific validation. Really appreciate you calling out those nuances, discussions like this are what help us all level up our Laravel code. :)

Collapse
 
xwero profile image
david duymelinck • Edited

I was thinking more along the lines of truly global settings (like UI themes),

I understand where you are coming from. But even then I think it should be handled by the code that needs the preferences.
In the case of the view, I would use a ViewComposer or ViewCreator in a ServiceProvider and only bind it to the templates that require it.

Thread Thread
 
xwero profile image
david duymelinck • Edited

I was thinking more about the ServiceProvider solution and I made the realization this could mean a lot of ServiceProvider classes each request has to go through to fulfill a request.

So I came up with an other solution. Extend the View facade.

use Illuminate\Support\Facades\View;

class UserPreferencesView extends View
{
    public static function make(string $view, array|\Illuminate\Contracts\Support\Arrayable $data = [], array $mergeData = []): \Illuminate\Contracts\View\View
    {
        $view = parent::make($view, $data, $mergeData);

        $view->with('userPreferences', auth()->check() ? auth()->user()->preferences : []);

        return $view;
    }

}
Enter fullscreen mode Exit fullscreen mode

And use it in the controller as you would use the default View facade.

Collapse
 
ldriko profile image
Heaven Aldrico

How would you handle user preferences in this case?

Collapse
 
xwero profile image
david duymelinck

I was reacting to @varzoeaa before i saw your comment. Please read that comment to know how I would handle it.

Collapse
 
nevodavid profile image
Nevo David

Pretty cool seeing middleware get this much love - real examples like these are what actually get me to try new stuff.