Laravel API Versioning: How to Version Your API

Hello Artisan, today I'll show you how to use Laravel API versioning. Versioning your Laravel API allows you to make changes to your API while ensuring backward compatibility for existing users. For all the popular services you know like Stripe or Dropbox, everyone uses API versioning. So, no more talk let's see how we can integrate API versioning into our application. 

Table of Contents

  1. URL-based Versioning
  2. Header-based Versioning
  3. Subdomain-based Versioning

URL-based Versioning

For the first example, we'll use our prefix() method of Route class. The most easier way for versioning. Let's see the example

routes/api.php
Route::prefix('v1')->group(function () {
    Route::get('users', 'UserController@index');
});

Route::prefix('v2')->group(function () {
    Route::get('users', 'UserController@indexV2');
});

Header-based Versioning

Here we need to create a middleware first. So, fire the below command in the terminal.

php artisan make:middleware ApiVersionMiddleware

This will generate a new middleware class named ApiVersionMiddleware in the app/Http/Middleware directory. Open the app/Http/Middleware/ApiVersionMiddleware.php file and update its handle method with the following code:

ApiVersionMiddleware.php
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class ApiVersionMiddleware
{
    public function handle(Request $request, Closure $next, $version)
    {
        $requestedVersion = $request->header('X-API-Version');

        if ($requestedVersion !== $version) {
            abort(404, 'API version not found.');
        }

        return $next($request);
    }
}

In this example, the middleware checks the value of the X-API-Version header and compares it with the version specified in the route middleware. If the versions don't match, a 404 response is returned. To make the middleware available for use, you need to register it in the Laravel middleware stack. Open the app/Http/Kernel.php file and add the following line to the $routeMiddleware property:

Kernel.php
protected $routeMiddleware = [
    // Other middleware entries...

    'api.version' => \App\Http\Middleware\ApiVersionMiddleware::class,
];

Now we can apply the api.version middleware to your routes to enable header-based versioning. Open your route file (routes/api.php) and modify your routes as follows:

routes/api.php
use App\Http\Controllers\UserController;

Route::middleware('api.version:v1')->group(function () {
    Route::get('users', [UserController::class, 'index']);
});

Route::middleware('api.version:v2')->group(function () {
    Route::get('users', [UserController::class, 'indexV2']);
});

Subdomain-based Versioning

The last step is one of the easiest and most preferable example, in my opinion. Here we need to create separate route files for each API version inside a new directory. For example, create a routes/v1 and routes/v2 directory, and within each directory, create an api.php file. Now open the RouteServiceProvider located at app/Providers/RouteServiceProvider.php. In the map() method, update it as follows: 

filename.ext
public function map()
{
    $this->mapApiRoutes();

    $this->mapV1Routes();
    $this->mapV2Routes();
}

protected function mapV1Routes()
{
    Route::prefix('v1')
        ->domain('v1.yourdomain.com')
        ->middleware('api')
        ->namespace($this->namespace)
        ->group(base_path('routes/v1/api.php'));
}

protected function mapV2Routes()
{
    Route::prefix('v2')
        ->domain('v2.yourdomain.com')
        ->middleware('api')
        ->namespace($this->namespace)
        ->group(base_path('routes/v2/api.php'));
}

And now we can define routes as follow

// routes/v1/api.php

Route::get('users', 'UserController@index');

// routes/v2/api.php

Route::get('users', 'UserController@indexV2');

The last thing I want to mention is that It's important to document your API versions and inform your clients about the available versions and any deprecations. Additionally, consider providing clear upgrade paths and versioning policies to ensure a smooth transition for your API consumers.

That's it for today. I hope it'll be helpful in upcoming projects. Thanks for reading. ๐Ÿ™‚