Версіонування API для веб-застосунку
Версіонування API — це спосіб внесення критичних змін без порушення існуючих клієнтів. Без версіонування будь-яка зміна схеми відповіді, видалення поля або перейменування ендпоїнту зламає мобільні застосунки, партнерські інтеграції та скрипти, які ви не контролюєте.
Стратегії версіонування
URL-версіонування — найбільш явний підхід:
GET /api/v1/articles
GET /api/v2/articles
Плюси: очевидно з URL, легко кешувати на CDN, просто для розробників. Мінуси: URL «засмічується» версією, ресурс /articles дублюється.
Header-версіонування:
GET /api/articles
Accept: application/vnd.myapp.v2+json
Чистіше з REST-перспективи, але складніше тестувати (curl потребує явного заголовка), погано кешується без Vary: Accept.
Query параметр:
GET /api/articles?version=2
Тільки для крайніх випадків — змішує версію з бізнес-параметрами запиту.
Рекомендація: URL-версіонування для більшості проектів. Header-версіонування, якщо API вже в production і URL не можна змінити.
Реалізація в Laravel
// routes/api.php
Route::prefix('v1')->group(base_path('routes/api_v1.php'));
Route::prefix('v2')->group(base_path('routes/api_v2.php'));
// routes/api_v2.php
Route::apiResource('articles', App\Http\Controllers\V2\ArticleController::class);
V2-контролери спадкують від V1, переопределяючи тільки змінені методи:
namespace App\Http\Controllers\V2;
use App\Http\Controllers\V1\ArticleController as V1Controller;
class ArticleController extends V1Controller
{
public function index(Request $request)
{
// V2: додали поле excerpt, прибрали body зі списку
return ArticleV2Resource::collection(
Article::paginate($request->per_page ?? 20)
);
}
}
Реалізація в NestJS
// main.ts
app.setGlobalPrefix('api');
// modules/v1/v1.module.ts та v2/v2.module.ts
@Controller('v1/articles')
export class ArticleV1Controller { ... }
@Controller('v2/articles')
export class ArticleV2Controller { ... }
Або через NestJS versioning API:
app.enableVersioning({ type: VersioningType.URI });
@Controller({ path: 'articles', version: '2' })
export class ArticleV2Controller {
@Get()
findAll() { ... }
}
Життєвий цикл версій
Типовий процес:
- Нова версія оголошується в
Changelogзі списком критичних змін - Стара версія отримує статус
deprecated— заголовокSunsetу відповідях - Через 6–12 місяців стара версія вимикається
// Middleware додає заголовок Deprecation до V1-відповідей
class AddDeprecationHeader
{
public function handle($request, Closure $next)
{
$response = $next($request);
if (str_starts_with($request->path(), 'api/v1/')) {
$response->headers->set('Deprecation', 'true');
$response->headers->set('Sunset', 'Sat, 01 Jan 2026 00:00:00 GMT');
$response->headers->set('Link', '<https://api.example.com/v2/>; rel="successor-version"');
}
return $response;
}
}
Що вважається критичною зміною
Не всі зміни потребують нової версії. Безпечні зміни (назад-сумісні):
- Додання нового поля у відповідь
- Додання нового необов'язкового параметра запиту
- Додання нового ендпоїнту
- Додання нового значення в enum (якщо клієнт ігнорує невідомі значення)
Критичні зміни, що потребують нової версії:
- Видалення поля з відповіді
- Перейменування поля
- Зміна типу поля (
string→integer) - Зміна формату (
2024-01-15→1705276800) - Видалення ендпоїнту
- Зміна семантики методу (наприклад, PATCH став поводитися як PUT)
API changelog
Версіонування без документування змін безцільне. Формат CHANGELOG.md для API:
## v2.0.0 (2025-03-01)
### Критичні зміни
- `GET /articles` — видалено поле `body`, додано `excerpt` (перші 200 символів)
- `POST /articles` — поле `tags` тепер масив ID, не рядків
### Нові функції
- `GET /articles/{id}/related` — схожі статті
- Cursor-based пагінація: параметр `after` замість `page`
## v1.x — Deprecated
Підтримується до 2026-01-01. Використовуйте v2.
Версіонування OpenAPI-специфікацій
Окремий файл на версію:
docs/
openapi.v1.yaml
openapi.v2.yaml
Або через $ref між файлами — переиспользування спільних схем (ErrorResponse, Pagination) без дублювання.
Терміни
Налаштування URL-версіонування з розводкою маршрутів, спадкоємством контролерів, Deprecation-заголовками: 2–3 дні. З автоматичним changelog, Sunset-мониторингом (алерт при перевищенні дедлайну версії) та окремими OpenAPI-файлами: 1 неділя.







