Версионирования API в Laravel

Версионирование API в Laravel - это способ управлять изменениями в интерфейсе так, чтобы новые функции и обновления не ломали существующих интеграций. Для этого разные версии (например, v1, v2) можно вынести в отдельные пространства: в URL (/api/v1/resource), в заголовки (Accept, X-Api-Version), либо в параметры запроса (?version=2). Такой подход позволяет развивать API, сохраняя обратную совместимость, и делает Laravel удобным инструментом для создания масштабируемых и надёжных RESTful-сервисов.


Как выглядит базовое версионирование в Laravel

Старый путь:

/api/users

С версионированием:

/api/v1/users

Позже выпускаешь новую версию:

/api/v2/users

И обе продолжают работать одновременно.


Настройка v1 (первой версии)

1. Структура папок

app/
└── Http/
    └── Controllers/
        └── Api/
            └── V1/
                └── UserController.php

2. Маршруты

routes/api.php:

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Api\V1\UserController;

Route::prefix('v1')->group(function () {
    Route::get('/users', [UserController::class, 'index']);
});

3. Контроллер V1

app/Http/Controllers/Api/V1/UserController.php:

namespace App\Http\Controllers\Api\V1;

use App\Http\Controllers\Controller;
use App\Models\User;

class UserController extends Controller
{
    public function index()
    {
        return response()->json([
            'version' => 'v1',
            'data' => User::all(),
        ]);
    }
}

Это базовая версия. Допустим, она возвращает всех пользователей.


Когда пора делать v2?

Типичные признаки:

  • изменилась структура данных

  • API должен возвращать другой формат

  • изменения ломают старых клиентов

  • логика устарела, её проще переписать

Например, в v2 хочется вернуть:

{
  "id": 1,
  "full_name": "John Doe",
  "email": "john@doe.com"
}

А раньше было:

{
  "id": 1,
  "first_name": "John",
  "last_name": "Doe",
  "email": "john@doe.com"
}

Добавляем v2 (вторую версию API)

1. Новая структура

app/
└── Http/
    └── Controllers/
        └── Api/
            ├── V1/
            └── V2/
                └── UserController.php

2. Маршруты

routes/api.php:

use App\Http\Controllers\Api\V2\UserController as UserControllerV2;

Route::prefix('v2')->group(function () {
    Route::get('/users', [UserControllerV2::class, 'index']);
});

3. Контроллер V2

namespace App\Http\Controllers\Api\V2;

use App\Http\Controllers\Controller;
use App\Models\User;

class UserController extends Controller
{
    public function index()
    {
        $users = User::all()->map(function ($user) {
            return [
                'id' => $user->id,
                'first_name' => $user->first_name,
                'last_name' => $user->last_name,
                'email' => $user->email,
            ];
        });

        return response()->json([
            'version' => 'v2',
            'data' => $users,
        ]);
    }
}

Теперь у API два действующих формата: старый (v1) и новый (v2).


Другие способы версионирования

1. Версионирование через заголовки

Подходит тем, кто хочет "чистые" URLs:

/api/users

И передаёт версию через header:

X-Api-Version: 2

Пример middleware:

namespace App\Http\Middleware;

use Closure;

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

        app()->bind('api-version', fn() => $version);

        return $next($request);
    }
}

И далее в маршрутах:

if (app('api-version') === 'v2') {
    require base_path('routes/api_v2.php');
} else {
    require base_path('routes/api_v1.php');
}

Подход гибкий, но сложнее, чем /v1, /v2.


2. Версионирование через query-параметры

Ещё один вариант, когда URL остаётся универсальным:

/api/users?version=2

PHP-обработчик:

$version = request()->query('version', '1');

Middleware-версия:

namespace App\Http\Middleware;

use Closure;

class QueryApiVersionMiddleware
{
    public function handle($request, Closure $next)
    {
        $version = $request->query('version', 'v1');

        app()->bind('api-version', fn() => $version);

        return $next($request);
    }
}

Switch по версиям:

if (app('api-version') === 'v2') {
    require base_path('routes/api_v2.php');
} else {
    require base_path('routes/api_v1.php');
}

Когда удобно использовать query-versioning?

  • при A/B тестах API

  • при временной поддержке бета-функций

  • когда клиентам сложно менять URL, но легко менять параметры

Комментарии (0)

Войдите, чтобы оставить комментарий