REST API, который не хочется переделывать через полгода

API — это контракт. Его нарушение стоит дорого: ломаются клиенты, нужно поддерживать несколько версий, а бизнес-логика расползается по местам, где её не ждали. Несколько решений, принятых в начале, сильно снижают этот риск.

Версионирование с первого дня

Даже если сейчас только один клиент и одна версия — добавьте /v1/ в URL уже сейчас. Это ничего не стоит на старте, но сэкономит много нервов потом, когда понадобится изменить структуру ответа, не сломав существующих интеграций.

Альтернативы — версионирование через заголовки (Accept: application/vnd.api.v1+json) или query-параметр — работают, но URL-версионирование проще для отладки и кеширования.

Единая структура ответа

Когда каждый эндпоинт возвращает данные в своём формате — это боль для всех, кто работает с API. Договоритесь о конверте: { data, meta, errors } и соблюдайте его везде.

Успешный ответ: { data: {...} }. Список: { data: [...], meta: { total, page, per_page } }. Ошибка: { errors: [{ code, message, field }] }. Простая схема, которую клиент может обработать универсально.

  • data — полезная нагрузка (объект или массив)
  • meta — пагинация, счётчики, вспомогательная информация
  • errors — массив ошибок с кодом, сообщением и опциональным полем
  • Никогда не меняйте тип data между запросами одного эндпоинта

HTTP-статусы по назначению

200 OK с { success: false } в теле — это анти-паттерн. Клиент должен уметь определять результат запроса по статус-коду, не разбирая тело. Используйте 201 Created при создании ресурса, 204 No Content при удалении, 422 Unprocessable Entity при ошибках валидации.

Не нужно знать все 50+ кодов — достаточно десятка самых частых. Главное — использовать их консистентно и не изобретать собственную систему поверх HTTP.

Пагинация: три подхода

Offset-пагинация (?page=2&per_page=20) — самая простая в реализации, подходит для большинства случаев. Минус: при активной записи страницы «сдвигаются», и один элемент может попасть дважды или не попасть вообще.

Cursor-пагинация (?after=eyJpZCI6MTAwfQ) — решает проблему сдвига, отлично работает для бесконечных лент. Но нельзя перепрыгнуть на произвольную страницу. Keyset-пагинация (?last_id=100) — компромисс: простая и стабильная, хотя и ограниченная в гибкости.

Обработка ошибок: думайте о клиенте

Ошибка должна объяснять, что пошло не так и — по возможности — что с этим делать. { code: 'EMAIL_TAKEN', message: 'Этот email уже используется' } намного полезнее, чем { error: 'Validation failed' }.

Для ошибок валидации возвращайте поле, к которому относится ошибка — тогда фронтенд сможет показать её рядом с нужным инпутом. Это мелочь, которая кардинально улучшает UX.

Источники и ссылки

Все статьи