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

Версионирование API — стратегия управления изменениями контракта API таким образом, чтобы существующие клиенты продолжали работать после выхода новых версий.

Зачем нужно

API — публичный контракт. Любое breaking change (переименование поля, изменение типа, удаление эндпоинта) ломает всех клиентов. Версионирование позволяет вводить несовместимые изменения без мгновенного разрушения экосистемы: старые клиенты остаются на v1, новые используют v2, даётся время на миграцию.

Где используется

  • Публичные REST API с внешними разработчиками (GitHub, Stripe)
  • Внутренние API при смене контракта между командами
  • Мобильные приложения (нельзя форсировать обновление у всех пользователей)
  • GraphQL — deprecation полей вместо версионирования

Стратегии версионирования

1. URL-версионирование (наиболее распространённое)

GET /api/v1/users
GET /api/v2/users

Плюсы: явное, легко тестировать, видно в логах
Минусы: «нарушает» принципы REST (URL должен идентифицировать ресурс)

2. Версия в заголовке Accept

GET /api/users HTTP/1.1
Accept: application/vnd.myapp.v2+json

# Ответ
HTTP/1.1 200 OK
Content-Type: application/vnd.myapp.v2+json

3. Кастомный заголовок

GET /api/users HTTP/1.1
API-Version: 2

4. Query-параметр (не рекомендуется)

GET /api/users?version=2

Реализация в Express

// Вариант 1: Отдельные роутеры для версий
const v1Router = require('./routes/v1');
const v2Router = require('./routes/v2');

app.use('/api/v1', v1Router);
app.use('/api/v2', v2Router);

// Вариант 2: Через заголовок
app.get('/api/users', (req, res) => {
  const version = req.headers['api-version'] || '1';
  if (version === '2') {
    return res.json(usersV2);
  }
  res.json(usersV1);
});

Семантическое версионирование (SemVer)

MAJOR.MINOR.PATCH
v2.1.3

MAJOR — breaking changes (переименование/удаление полей, смена структуры)
MINOR — обратно совместимые дополнения (новые поля, новые эндпоинты)
PATCH — исправления багов без изменения контракта

Для публичных API версионируют по MAJOR: v1, v2, v3

Breaking vs Non-breaking изменения

✅ Non-breaking (обратно совместимые):
   - Добавление нового поля в ответ
   - Добавление нового необязательного параметра
   - Добавление нового эндпоинта
   - Ослабление валидации (было required → стало optional)

❌ Breaking (ломающие):
   - Переименование поля (name → fullName)
   - Изменение типа (string → array)
   - Удаление поля или эндпоинта
   - Изменение формата (дата как "2024-01-15" → Unix timestamp)
   - Ужесточение валидации

Стратегия deprecation

# Предупреждение об устаревании через заголовки
HTTP/1.1 200 OK
Deprecation: true
Sunset: Sat, 01 Jan 2026 00:00:00 GMT
Link: <https://api.example.com/v2/users>; rel="successor-version"

Частые ошибки

  • Нет версионирования с самого начала — потом невозможно добавить без боли
  • Breaking changes в patch/minor релизах — клиенты ломаются без предупреждения
  • Бесконечная поддержка всех версий — создаёт технический долг
  • Не уведомляют клиентов о deprecation заблаговременно (минимум 6 месяцев)

Связанные темы

Ресурсы