f in x
Laravel 12: App Structure and What's New Compared to Previous Versions
> cd .. / HUB_EDITORIALE
Analisi dei dati e metriche

Laravel 12: App Structure and What's New Compared to Previous Versions

[2026-05-30] Author: Ing. Calogero Bono

You just ran laravel new project and found an almost empty app directory. No Http/Kernel.php, no Console/Kernel.php, no Providers/RouteServiceProvider. If you come from Laravel 10 or earlier, it's normal to feel lost. But don't worry: this cleanup is good news. We at Meteora Web spent months migrating dozens of projects from the old structure to the new one, and we can tell you the result is more maintainable, faster to configure, and less noisy. In this guide we explain exactly what changes in Laravel 12 compared to previous versions, how the new bootstrap/app.php works, and how to adapt your habits.

Why Laravel revolutionized the app structure

The framework grew. With each version, Laravel accumulated configuration files, providers and classes that many developers never touched. Laravel 11 cleaned house: it moved almost all boot logic into the new bootstrap/app.php file and removed the files that were created by default but left untouched. Laravel 12 inherits and strengthens this philosophy. The result? Fewer files to manage, less confusion, and a faster learning curve for newcomers. For us, coming from accounting and balance sheets, it's like moving from a ledger full of redundant entries to a clean statement: substance over fluff.

Key differences from Laravel 10 and earlier

1. Goodbye Http/Kernel and Console/Kernel

In Laravel 10, every request went through app/Http/Kernel.php handling global middleware, middleware groups and priorities. In Laravel 12, all of this is centralized in bootstrap/app.php. The same goes for app/Console/Kernel.php: Artisan commands and scheduling are now configured directly in the bootstrap or via traits in the commands themselves.

// Laravel 12 – bootstrap/app.php
return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        api: __DIR__.'/../routes/api.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware) {
        $middleware->web(prepend: [
            \App\Http\Middleware\ForceJson::class,
        ]);
        $middleware->api(remove: [
            \Illuminate\Routing\Middleware\ThrottleRequests::class,
        ]);
    })
    ->withExceptions(function (Exceptions $exceptions) {
        //
    })->create();

This syntax is more declarative. You can add global middleware, custom groups and aliases directly in the file you already know. No separate Kernel file to hunt for.

2. Streamlined Service Providers

Before, every new project included AppServiceProvider, AuthServiceProvider, BroadcastServiceProvider, EventServiceProvider and RouteServiceProvider. In Laravel 12, most of these providers are gone by default. Only AppServiceProvider (empty) remains, and RouteServiceProvider no longer exists: routes are registered via the withRouting array in bootstrap/app.php. Events and listeners are configured in the new discoverEvents array or in the provider's boot method. We love this simplification: less boilerplate code to maintain.

3. Leaner migrations and factories

Default migrations now include only create_users_table and create_personal_access_tokens_table. The password_resets, failed_jobs and job_batches tables are no longer created automatically. If you need them, just run php artisan make:migration with the appropriate flag. Factories are auto-discovered thanks to Laravel 11/12's discovery feature, so no manual path registration.

4. Transparent exception handling

The old App\Exceptions\Handler is gone. Exception handling is configured directly in the withExceptions method of bootstrap/app.php. This makes it immediately visible what you do with exceptions, without opening another file. Example:

->withExceptions(function (Exceptions $exceptions) {
    $exceptions->report(function (CustomException $e) {
        // ...
    });
    $exceptions->render(function (CustomException $e) {
        return response()->json(['error' => $e->getMessage()], 400);
    });
})

5. Built-in health check route /up

Laravel 12 includes a preconfigured health check route (/up) that returns a 200 OK if the application is alive. Useful for load balancers and monitoring. No extra setup: it's already enabled if you use withRouting with the health key.

What stays the same (and what changes in practice)

Not everything is overturned. Controllers still live in app/Http/Controllers, models in app/Models, custom Artisan commands in app/Console/Commands. Views in resources/views. Routes in routes/*. The core idea hasn't changed.

But the app structure is flatter and lighter. If you're starting a new project, you no longer have to delete unused default files. If you're migrating from Laravel 10, prepare to move kernel and provider logic into bootstrap/app.php. A practical tip: don't try to force the old structure into the new one. Instead, get comfortable with the new withMiddleware and withExceptions. We at Meteora Web found that the initial adjustment effort pays off in code cleanliness and debugging ease.

Practical example: adding an API authentication middleware in Laravel 12

In Laravel 10 you would have edited app/Http/Kernel.php by adding a line in the $routeMiddleware array. Today you do this:

// bootstrap/app.php
use App\Http\Middleware\EnsureTokenIsValid;

->withMiddleware(function (Middleware $middleware) {
    $middleware->alias([
        'valid.token' => EnsureTokenIsValid::class,
    ]);
    $middleware->api(prepend: [
        EnsureTokenIsValid::class,
    ]);
})

Note: aliases are registered in the same place, not in two different files. Everything is centralized.

Handling events in Laravel 12

In Laravel 10 you had to create EventServiceProvider and register events and listeners in the $listen property. Now you can do it in two ways: either use automatic event discovery (default) or register manually in your AppServiceProvider. Automatic discovery looks for events and listeners in app/Events and app/Listeners. You can disable or customize it in bootstrap/app.php:

'events' => [
    'discover' => true,
    'map' => [
        \App\Events\OrderShipped::class => [
            \App\Listeners\SendShipmentNotification::class,
            \App\Listeners\UpdateInventory::class,
        ],
    ],
],

Personally, we prefer manual mapping for complex projects: it gives control and transparency.

How to upgrade a Laravel 10 project to Laravel 12

If you have a legacy project, the upgrade process is guided by Laravel Shift or the official documentation. The critical points are:

  • Remove superfluous files: delete app/Http/Kernel.php, app/Console/Kernel.php, app/Exceptions/Handler.php, app/Providers/RouteServiceProvider.php and other providers no longer needed.
  • Move configuration: transfer middleware, aliases, priorities and groups into bootstrap/app.php.
  • Review exceptions: move report and render logic from Handler to withExceptions.
  • Update commands: if you used scheduled commands, ensure they are now registered in the withCommands array or use the Schedule trait.

We recommend testing every single step with a copy of the project locally. We've seen subtle bugs when middleware aliases or exception bindings are forgotten.

Why this structure is better even for developers coming from other frameworks

If you come from Symfony or Node.js, you'll find the new bootstrap more familiar: everything boils down to a single central file orchestrating middleware, routing and error handling. No more two-tier files-application. Laravel aligns with the standard of modern applications that use a centralized bootloader (think Vite or Angular). This reduces friction for new developers and makes the framework more approachable.

In summary — what to do now

  1. Create a new Laravel 12 project: composer create-project laravel/laravel:^12.0 try-laravel12 and explore the structure.
  2. Read the bootstrap/app.php file: comment each method to understand what it does. Try adding a middleware alias.
  3. Migrate a test project: take an old Laravel 10 project, follow the steps above and verify everything works.
  4. Update your team's internal documentation: explain the differences with a concrete middleware and routing example.
  5. Check your hosting environment: ensure the server has PHP 8.3+ and required extensions (BCMath, Ctype, Fileinfo, JSON, Mbstring, OpenSSL, PDO, Tokenizer, XML).

Laravel 12's structure is not just a cosmetic cleanup: it's a paradigm shift that makes your application easier to understand and maintain. We embraced it from day one. You should too.

Sponsored Protocol

Ing. Calogero Bono

> AUTHOR_EXTRACTED

Ing. Calogero Bono

Co-founder di Meteora Web. Ingegnere informatico, sviluppo ecosistemi digitali ad alte prestazioni. AI, automazione, SEO tecnica e infrastrutture web. Scrivo di tecnologia per rendere complesso… semplice.

[ Read Full Dossier ]

Hai bisogno di applicare questa strategia?

Esegui il protocollo di contatto per iniziare un progetto con noi.

> INIZIA_PROGETTO

Sponsored

> MW_JOURNAL

> READ_ALL()