Laravel 12: Structured Route Files, Slim Skeletons, and the New Application Bootstrapping
#laravel #laravel-12 #upgrade #routing #php

Laravel 12: Structured Route Files, Slim Skeletons, and the New Application Bootstrapping

3 min read Mohamed Said Mohamed Said

What Actually Changed in Laravel 12

Laravel 12 is not a paradigm shift — it is a focused refinement. The headline changes are a slimmer default skeleton, a cleaner route file layout, a revised bootstrap/app.php contract, and the removal of several long-deprecated helpers. If you have kept up with 10 and 11, the upgrade is straightforward. If you skipped 11, read on carefully.


The Slim Skeleton

Laravel 11 introduced the condensed skeleton that dropped app/Http/Kernel.php and app/Providers/RouteServiceProvider.php. Laravel 12 doubles down: a fresh laravel new project ships with fewer stub files and a single bootstrap/app.php that owns middleware registration, exception handling, and routing in one place.

// bootstrap/app.php — Laravel 12 default
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(append: [
            \App\Http\Middleware\HandleInertiaRequests::class,
        ]);
    })
    ->withExceptions(function (Exceptions $exceptions) {
        $exceptions->render(function (\App\Exceptions\DomainException $e, Request $request) {
            return response()->json(['error' => $e->getMessage()], 422);
        });
    })
    ->create();

The withRouting call now accepts a then closure for anything dynamic — useful for module-based route loading without a custom service provider.

->withRouting(
    web: __DIR__.'/../routes/web.php',
    then: function () {
        Route::middleware('api')
            ->prefix('webhooks')
            ->group(base_path('routes/webhooks.php'));
    },
)

Structured Route Files by Convention

The routes/ directory now ships with web.php, api.php, console.php, and channels.php. Each is auto-loaded based on the withRouting declaration — no manual require or service provider needed. This is not new functionality, but the skeleton now enforces the pattern from day one.

For modular monoliths, the then closure is the idiomatic hook:

then: function () {
    foreach (glob(base_path('modules/*/routes.php')) as $file) {
        Route::middleware('web')->group($file);
    }
},

Removed Deprecated Helpers

Several helpers deprecated in Laravel 9–11 are gone:

  • Str::replaceArray() — use str_replace with an array.
  • Route::prefix() chained directly on the facade without a group closure.
  • The dispatchNow alias on the Bus facade — use dispatchSync.

Run php artisan about and review your composer.json for packages that may still call these internally.


Upgrading from Laravel 11

1. Update composer.json

"laravel/framework": "^12.0"

Run composer update and address any immediate conflicts before touching application code.

2. Publish the new stubs

php artisan stub:publish

Compare your existing bootstrap/app.php against the new default. If you are on Laravel 11 already, the diff is minimal.

3. Audit deprecated usages

grep -rn 'dispatchNow\|replaceArray' app/ routes/

Replace each hit before deploying.

4. Check package compatibility

Filament v3/v4, Spatie packages, and most first-party packages had 12-compatible releases within days of the framework tag. Pin to their latest minor before upgrading the framework in production.


PHP Version Requirements

Laravel 12 requires PHP 8.2 at minimum, with full support for 8.3 and 8.4. If you are still on 8.1, upgrade PHP first — the framework will refuse to install.


Key Takeaways

  • bootstrap/app.php is now the single source of truth for middleware, routing, and exception rendering.
  • The withRouting(then:) closure is the clean hook for module-based or conditional route loading.
  • Several long-deprecated helpers are removed — audit with grep before upgrading.
  • PHP 8.2+ is required; test on 8.3 to benefit from typed class constants and improved readonly semantics.
  • The upgrade from Laravel 11 is low-risk; from Laravel 10 it requires the 11 skeleton migration first.

Found this useful?

Frequently Asked Questions

3 questions
Q01 Can I upgrade directly from Laravel 10 to Laravel 12?
Not in a single step. Laravel 10 uses the old Kernel and RouteServiceProvider structure. You should migrate to the Laravel 11 skeleton first — adopting bootstrap/app.php — then bump to 12. Attempting to skip 11 means doing both migrations at once, which increases risk.
Q02 Does Laravel 12 change how Filament panels are bootstrapped?
No. Filament registers its panels via a service provider that is still resolved through the standard container. The bootstrap/app.php changes only affect how the framework's own middleware and routing are declared, not third-party service providers.
Q03 Is the withRouting 'then' closure the right place for module route loading?
Yes. The 'then' closure runs after the named route files are loaded, giving you full access to the Route facade. It is the idiomatic replacement for a custom RouteServiceProvider in modular monolith setups.

Continue reading

More Articles

View all