0

Creating middleware — handle($request, $next)

Beginner5 min read·lv-07-002

Concept

Creating custom middleware in Laravel is a two-step process: generate the class with artisan, then register it in the HTTP kernel.

php artisan make:middleware MiddlewareName: Creates app/Http/Middleware/MiddlewareName.php with a stub. The method signature is handle(Request $request, Closure $next): Response.

The $next callable: Calling $next($request) passes the request to the next middleware in the pipeline (or the controller if there's no more middleware). It returns the response from everything downstream. You can modify the request before passing it, and modify the response after.

Middleware parameters: Middleware can accept additional parameters via handle(). In the route definition, pass with a colon: ->middleware('role:admin'). In the method: handle(Request $request, Closure $next, string $role): Response.

Multiple parameters: ->middleware('role:admin,editor') passes $role = 'admin' and $role2 = 'editor' as separate arguments.

Accessing the container in middleware: Middleware is resolved from the container — inject dependencies in the constructor.

Response manipulation: After $next($request) returns, you have the response object. Modify headers, replace content, or wrap the response.

Code Example

php
<?php
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpFoundation\Response;

// Basic auth middleware
class EnsureAuthenticated
{
    public function handle(Request $request, Closure $next): Response
    {
        if (!Auth::check()) {
            if ($request->expectsJson()) {
                return response()->json(['message' => 'Unauthenticated'], 401);
            }
            return redirect()->route('login');
        }
        return $next($request);
    }
}

// Middleware with parameter
class RequireRole
{
    public function handle(Request $request, Closure $next, string ...$roles): Response
    {
        $user = Auth::user();
        if (!$user || !in_array($user->role, $roles)) {
            abort(403, 'Insufficient permissions');
        }
        return $next($request);
    }
}
// Route: ->middleware('role:admin,editor')
// handle($request, $next, 'admin', 'editor') — $roles = ['admin', 'editor']

// Middleware with injected dependency
class CheckSubscription
{
    public function __construct(
        private readonly \App\Services\SubscriptionService $subscriptions,
    ) {}

    public function handle(Request $request, Closure $next): Response
    {
        $user = Auth::user();
        if ($user && !$this->subscriptions->isActive($user)) {
            return response()->json(['error' => 'Subscription required'], 402);
        }
        return $next($request);
    }
}

// Timing middleware — measure request duration
class MeasureRequestTime
{
    public function handle(Request $request, Closure $next): Response
    {
        $start = microtime(true);

        $response = $next($request);

        $duration = round((microtime(true) - $start) * 1000, 2);
        $response->headers->set('X-Response-Time', "{$duration}ms");

        if ($duration > 500) {
            logger()->warning("Slow request: {$duration}ms", ['url' => $request->path()]);
        }

        return $response;
    }
}