DEV Community

Grant
Grant

Posted on

Exploring Middleware in Laravel 11

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 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 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:

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();
Enter fullscreen mode Exit fullscreen mode

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 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 by calling alias():

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

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.

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,
    ]);
}
Enter fullscreen mode Exit fullscreen mode

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

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,
    ]);
}
Enter fullscreen mode Exit fullscreen mode

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

function (Middleware $middleware) {
    $middleware->appendToGroup('web', \App\Http\Middleware\MyMiddleware::class);
}
Enter fullscreen mode Exit fullscreen mode

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.

I've created a class in app/Http called AppMiddleware.php. As old habits die hard, you might call this Kernel.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);
    }
}
Enter fullscreen mode Exit fullscreen mode

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

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();
Enter fullscreen mode Exit fullscreen mode

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?

Top comments (10)

Collapse
 
blade93ny profile image
ZettaV

i think this is a bad change... i understand that for some small API based projects the Slim skeleton makes sense, but for most people using the MONOLITH structure and using Inertia or Blade components or whatever it doesnt make sense to have to execute a TON of additional commands to publish each individual config file and then install api and this and that...WHY???
They way i see it, there should be 2 Scaffoldings in Laravel... one is default one that publishes everything as was done always, and the other would be something like laravel new ---scaffold=slim which would give you this Slim structure... That makes more sense to me as a dev...
In any case, in order for this new structure and doing things via Bootstrap/app they will need to create a very detailed DOCs for how to properly use it or people are going to HATE IT...

Collapse
 
perisicnikola37 profile image
Nikola Perišić

Agree

Collapse
 
back2lobby profile image
8ack2Lobby

I don't like this approach of handling middleware tbh, rest is fine.

Collapse
 
zephni profile image
Craig Dennis • Edited

Thanks for the info, I've only just finished upgrading all my work sites to Laravel 10 😳

I kind of like the idea of the default project size being smaller, publishing only the config you need is not a bad thing. The Kernal change is a very unexpected one though, although it does make sense actually moving this functionality into bootstrap/app.php, but I'm with you on the idea that it's not a good thing to "hide" what is default... it makes you feel like you're less in control, and what if you wanted to replace some default of some kind, I'm sure there's a way but it wouldn't be intuitive.

It feels like some new people learning Laravel may feel more lost and feel like there's alot of magic going on under the hood, in some cases there's already a little too much magic.

I hope they change this a little. Like you showed the __invoke example, I wouldn't mind if they had a default invokable class for each withX in app.php, so that separates out the concerns nicely, but go back to showing all of the default middleware etc within those, giving control back to the developer in a simple yet seperated way.

Collapse
 
perisicnikola37 profile image
Nikola Perišić

Great article :)
What I find most appealing about this Laravel update is the avoidance of exposing unnecessary config files that I never even touch. If there's something I need to configure, I can simply publish and modify them as necessary.

Collapse
 
eldair profile image
Krisitjan

As you said, hiding default middlewares is not really a good idea, if they were all listed here by default than it would be better (even though I still prefer the current system).

Collapse
 
emiliovardev profile image
Emilio Vargas Millán

From what I can see, the Middleware class contains all the default middleware plus the methods that can be accessed from app.php.

In my opinion, it is a more organized way to manage them and hide files that, from my point of view, we are not going to use.

In the end, if someone wants to delve deeper into middleware, I assure you they will get to the root xD

Collapse
 
robertobutti profile image
Roberto B.

Great article! For the upcoming Laravel 11, articles like this one are "pure gold" :)

Collapse
 
ejntaylor profile image
Elliot Taylor

Really good article - thanks for sharing

Collapse
 
techsolutionstuff profile image
Techsolutionstuff

Thank you so much for the awesome article. Also, learn How to Customize Default Middleware in Laravel 11