Cómo versionar una API en Laravel

Dos formas diferentes de versionar una API en Laravel: require y el RouteServiceProvider

Cómo versionar una API en Laravel
Photo by Tim Wildsmith / Unsplash

Versionar las APIs es siempre una buena práctica para prepararnos para el futuro, añadiendo soporte para breaking changes sin tener que cambiar la aplicación, simplemente cambiando el v1 de la url a un v2.

Hacer esto en Laravel es bastante sencillo y podemos hacerlo, como siempre, de varias formas. La más sencilla y la que suelo usar es esta:

En primer lugar, creo un fichero routes/api/v1.php, donde añado las rutas de mi v1 de la API de la siguiente forma:

<?php

use Illuminate\Support\Facades\Route;

Route::prefix('v1')->group(function () {
    // añadir las rutas aquí
});

Luego, en nuestro fichero routes/api.php añadimos lo siguiente:

<?php

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/

require __DIR__.'/api/v1.php';
// require __DIR__.'/api/v2.php'; // cuando tengamos más versiones

Este es el enfoque más sencillo y funcional. Sin embargo, existe otra forma de hacerlo desde nuestro RouteServiceProvider. En este Provider es donde se registra que el fichero api.php se usa para la API, y el fichero web.php se usa para la web, por lo que podemos modificarlo un poco para añadir el soporte directamente desde aquí:

<?php

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 function boot()
    {
        $this->configureRateLimiting();

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

            // ...
        });
    }
    
    // ...
}

De esta forma, podríamos incluso eliminar el fichero api.php, aunque normalmente prefiero usar el primer enfoque ya que resulta más fácil y entendible para newcomers en el proyecto.