Exploring Middleware in Laravel 11
Tags: laravel, discuss
Published: 2024-01-04 05:00:19

Laravel 11 is set to release in "Q1" of 2024, which may be as soon as next month.

I am starting a new project, and because it's so close to the release date I figured I would take a look at what's going to be different with the new major release. I remember reading a [Laravel News article](https://laravel-news.com/laravel-11) 6 months ago about how the Http Kernel was going away and didn't think much of it.

When I created the project using `laravel new project --dev`, I was quite surprised at how much smaller the project size was. It was very surprising to see an empty `config` folder (you can publish the config files with `php artisan config:publish`)!

And sure enough, there is no Http Kernel. So… how do you add or change middleware? Prior to Laravel 11, the Http Kernel (found in `app/Http/Kernel.php`) is where all the configuration for [middleware](https://laravel.com/docs/10.x/middleware) was found. Also prior to Laravel 11, you would typically never touch `bootstrap/app.php`. However, that is no longer the case in this new version.

To be honest, as a Laravel user since v4.2, this is quite the paradigm shift. Instead of an easy-to-read `Kernel.php` file, there is a lot more implicit knowledge you need before adding middleware.

The new starting point for `bootstrap/app.php` is as follows:

```php
return Application::configure()
    ->withProviders()
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        // api: __DIR__.'/../routes/api.php',
        commands: __DIR__.'/../routes/console.php',
        // channels: __DIR__.'/../routes/channels.php',
    )
    ->withMiddleware(function (Middleware $middleware) {
        //
    })
    ->withExceptions(function (Exceptions $exceptions) {
        //
    })->create();
```

I am just exploring middleware in this post, but as you can see this is quite a different approach than we've seen historically. I sat there scratching my head, "How do I set up my own middleware? How do I change the defaults?" I had to explore the [`Illuminate\Foundation\Configuration\Middleware`](https://github.com/laravel/framework/blob/master/src/Illuminate/Foundation/Configuration/Middleware.php) class to find out.

Calling `Application::configure()` returns an instance of `Illuminate\Foundation\Configuration\ApplicationBuilder`, where it then calls the various functions, `withProviders()`, `withRouting()` and the `withMiddleware()` functions. The `withMiddleware()` function takes a `callable`.

Using the boilerplate, we can add new [middleware aliases](https://laravel.com/docs/10.x/middleware#assigning-middleware-to-routes) by calling `alias()`:

```php
function (Middleware $middleware) {
    $middleware->alias([
        'some_key' => \App\Http\Middleware\MyMiddleware::class,
    ]);
}
```

Once this `some_key` is added, we can assign it to individual routes and route groups. If we want to add middleware to every request, we can use the `append()` or `prepend()` functions to add global middleware.

```php
function (Middleware $middleware) {
    // Using a string
    $middleware->append(\App\Http\Middleware\MyMiddleware::class);
    
    // Or adding multiple
    $middleware->append([
        \App\Http\Middleware\MyMiddleware::class,
        \App\Http\Middleware\MyOtherMiddleware::class,
    ]);
}
```

We can remove middleware that's there by default by calling the `remove()` function.

```php
function (Middleware $middleware) {
    // Using a string
    $middleware->remove(\Illuminate\Http\Middleware\ValidatePostSize::class);
    
    // Or removing multiple default middleware
    $middleware->remove([
        \Illuminate\Http\Middleware\TrustProxies::class,
        \Illuminate\Http\Middleware\HandleCors::class,
    ]);
}
```

We can add or remove middleware to specific groups, i.e. the `web` group using the `appendToGroup()`/`prependToGroup()` and `removeFromGroup()` functions.

```php
function (Middleware $middleware) {
    $middleware->appendToGroup('web', \App\Http\Middleware\MyMiddleware::class);
}
```

If/when this `bootstrap/app.php` file gets a bit messy (which I imagine it will), we can clean it up by moving this stuff to an [invokable class](https://www.amitmerchant.com/invokable-classes-php/).

I've created a class in `app/Http` called `AppMiddleware.php`. As old habits die hard, you might call this `Kernel.php`…

```php
<?php

namespace App\Http;

use Illuminate\Foundation\Configuration\Middleware;

class AppMiddleware
{
    public function __invoke(Middleware $middleware)
    {
        $middleware->appendToGroup('web', \App\Http\Middleware\MyMiddleware::class);
    }
}
```

Now in `bootstrap/app.php`, replace the closure with a new instance of our invokable class.

```php
return Application::configure()
    ->withProviders()
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        // api: __DIR__.'/../routes/api.php',
        commands: __DIR__.'/../routes/console.php',
        // channels: __DIR__.'/../routes/channels.php',
    )
    ->withMiddleware(new \App\Http\AppMiddleware())
    ->withExceptions(function (Exceptions $exceptions) {
        //
    })->create();
```

Like all new things, change is hard. I have a lingering question with this change. We have all the same tooling and ability, but it's a bit more difficult, at least at first, to understand how to manage it all.

I feel like this change will require a lot more implicit knowledge of the built-in middleware. Does it matter? Probably not. But in some cases, you might need to remove or replace a default middleware. This requires you actually _know_ what is there by default. Not just in the global middleware, but in groups and aliased middleware. Personally I feel like this change increases the learning curve. Even I forget what's there by default or what the aliases are and reference the `app/Http/Kernel.php` file regularly.

I question whether the lack of verbosity and smaller file footprint is worth it?

What are your thoughts with this new change?