Mastering Laravel's Terminable Middleware: Post-Response Magic

Mastering Laravel's Terminable Middleware: Post-Response Magic

In the world of web development, timing is everything. Sometimes, you need to perform actions after the response has been sent to the browser. Enter Laravel's terminable middleware - a powerful feature that allows you to execute code after the HTTP response has been dispatched. Let's dive into how you can leverage this feature to optimize your Laravel applications.

Understanding Terminable Middleware

Terminable middleware in Laravel allows you to define a terminate method that is called after the response has been sent to the browser. This is particularly useful for tasks that don't need to delay the response to the client, such as certain types of logging, cleanup operations, or firing off queued jobs.

Creating Terminable Middleware

Here's how you can create a terminable middleware:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class TerminatingMiddleware
{
    public function handle(Request $request, Closure $next): Response
    {
        return $next($request);
    }

    public function terminate(Request $request, Response $response): void
    {
        // Post-response logic here
    }
}

The terminate method receives both the request and the response, allowing you to perform actions based on the final state of both.

Registering Terminable Middleware

To use terminable middleware, add it to your global middleware stack or route middleware in bootstrap/app.php:

->withMiddleware(function (Middleware $middleware) {
    $middleware->append([
        \App\Http\Middleware\TerminatingMiddleware::class,
    ]);
})

Ensuring Consistent State

By default, Laravel resolves a fresh instance of the middleware for the terminate method. If you need to maintain state between handle and terminate, register the middleware as a singleton in your AppServiceProvider:

use App\Http\Middleware\TerminatingMiddleware;

public function register(): void
{
    $this->app->singleton(TerminatingMiddleware::class);
}

Real-Life Example

Let's consider a scenario where we want to log API usage without slowing down our responses. We'll create a terminable middleware that logs the request details and response time after the response has been sent:

<?php

namespace App\Http\Middleware;

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

class ApiUsageLogger
{
    protected $startTime;

    public function handle(Request $request, Closure $next): Response
    {
        $this->startTime = microtime(true);
        return $next($request);
    }

    public function terminate(Request $request, Response $response): void
    {
        $duration = microtime(true) - $this->startTime;
        
        Log::info('API Call', [
            'url' => $request->fullUrl(),
            'method' => $request->method(),
            'status' => $response->getStatusCode(),
            'duration' => round($duration * 1000, 2) . 'ms',
            'ip' => $request->ip(),
        ]);
    }
}

Now, let's apply this middleware to our API routes:

Route::middleware(['api', ApiUsageLogger::class])->group(function () {
    Route::get('/users', [UserController::class, 'index']);
    // Other API routes...
});

With this setup, every API call will be logged after the response has been sent, without impacting the response time. Here's what the log entry might look like:

{
    "message": "API Call",
    "context": {
        "url": "https://api.example.com/users",
        "method": "GET",
        "status": 200,
        "duration": "45.67ms",
        "ip": "192.168.1.1"
    }
}

By mastering terminable middleware, you can optimize your Laravel applications, improving performance and maintainability. Whether you're logging, cleaning up resources, or triggering background processes, terminable middleware provides a clean, efficient way to handle post-response operations.

If you found this guide helpful, don't forget to subscribe to my daily newsletter and follow me on X/Twitter for more Laravel tips and tricks!

Subscribe to Harris Raftopoulos

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe