API Versioning in Laravel 11

API Versioning in Laravel 11

Why Version Your Laravel App?

As a backend engineer, versioning your app is an essential workflow in development. It allows you to make necessary updates without disrupting existing environments or breaking earlier versions of your API. Versioning also helps frontend engineers know exactly which version of the API they’re working with, promoting smoother collaboration.

Versioning in Laravel 11 vs. Earlier Versions

In previous Laravel versions, API versioning was typically configured in the RouteServiceProvider.php file located in the app/Providers directory. Here's what the setup looked like:

namespace App\Providers;

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Route;

class RouteServiceProvider extends ServiceProvider
{
    public const HOME = '/home';

    public function boot(): void
    {
        RateLimiter::for('api', function (Request $request) {
            return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
        });

        $this->routes(function () {
            Route::middleware('api')
                ->prefix('api')
                ->group(base_path('routes/api.php'));

            Route::middleware('web')
                ->group(base_path('routes/web.php'));
        });
    }
}

Key Breakdown:

  1. $this->routes(function () {...});: Loads and groups routes for both web and API.

  2. Route::middleware('api'): Applies api middleware (handles rate limiting and request formatting).

  3. ->prefix('api'): Prefixes all API routes with /api.

  4. ->group(base_path('routes/api.php')): Loads API routes from routes/api.php.

  5. Route::middleware('web'): Loads web-based routes.

  6. ->group(base_path('routes/web.php')): Loads web routes from routes/web.php.

Versioning in Laravel 11

Laravel 11 introduces changes aimed at reducing unnecessary files and providing better abstraction. As a result, RouteServiceProvider.php is no longer scaffolded by default. Instead, versioning configuration happens in bootstrap/app.php.

Here’s how the new setup looks:

use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        api: __DIR__.'/../routes/api.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
        then: function (){
            Route::middleware('api')
                ->prefix('api/v1')
                ->group(__DIR__.'/../routes/api_v1.php');
        }
    )
    ->withMiddleware(function (Middleware $middleware) {
        //
    })
    ->withExceptions(function (Exceptions $exceptions) {
        //
    })->create();

Key Breakdown:

  1. web: __DIR__.'/../routes/web.php': Loads web routes.

  2. api: __DIR__.'/../routes/api.php': Loads API routes.

  3. commands: __DIR__.'/../routes/console.php': Loads Artisan console commands.

  4. health: '/up': Sets up a health check endpoint.

  5. then: function () {...}: Adds versioning logic for API routes. It creates a versioned group (api/v1).

This method allows flexibility and maintains API versioning control.

Generating API Routes

To start, you need to generate the api.php file, which isn’t scaffolded by default in Laravel 11. Run:

php artisan install:api

This will create the routes/api.php file. Now, copy this file and rename it to api_v1.php to match the version prefix used in bootstrap/app.php. This file will handle all your version 1 (/api/v1/) endpoints.

Next, define your routes inside api_v1.php:

Route::apiResource('tickets', TicketController::class);

The apiResource method generates routes for API-specific CRUD operations in a streamlined manner, excluding web-specific routes like create and edit.

Creating the Controller

Run the following command to create the necessary controller:

php artisan make:controller "Api/V1/TicketController" --resource --model=Ticket --request

This command generates a resource controller (TicketController) in the Api/V1 namespace, with CRUD methods linked to the Ticket model. The --request flag also generates a form request class for handling validation.

Testing Your API

You can now test your versioned API in Postman by using endpoints like:

http://ticketeer.test/api/v1/tickets

I hope now you get to know how to build and test versioned APIs in Laravel 11.

I’m sharing what I’ve learned about Laravel over the past few weeks as I’ve been getting familiar with it. For those with more experience in Laravel, feel free to share your feedback or how you’ve approached versioning in your own projects!