Дизайн HTTP API

Используемые стандарты

Ресурсы и URL

  • URL идентифицирует ресурс. Имена – существительные во мн. числе: /users, /articles

  • Для единственного «персонального» ресурса допустимы формы: /profile, /basket

  • Вложенные ресурсы стараемся не использовать. Предпочтение – фильтрации:

    • /products?category=food&category=health

    • /category/food/products

  • Множественная выборка по id: GET /contents?id=1&id=2&id=3

Параметры запроса

  • Невалидные значения параметров → 400 (Problem Details)

  • Имя параметра одинаково при одиночном и множественном значении

  • Операторы сравнения: __gt, __gte, __lt, __lte

Методы и операции

Метод

Коллекция /resources

Элемент /resources/{id}

GET

Список, фильтрация, пагинация

Чтение ресурса

POST

Создание

Действия/подресурсы, если требуется

PUT

Массовые опер. (редко), либо полная замена

Полная замена ресурса (idempotent)

PATCH

Частичное обновление (JSON Patch application/json-patch+json или JSON Merge Patch application/merge-patch+json)

DELETE

Массовое удаление (редко)

Удаление ресурса (idempotent)

Действия над ресурсами

  • По возможности моделируем как ресурсы/состояния. Если действие уместно – используем подпуть /actions:

    • POST /users/{id}/actions/deactivate

  • Действие должно быть идемпотентным или клиент присылает Idempotency-Key (рекомендовано к поддержке) для безопасных повторов

Контент

  • Единый формат: application/json; charset=utf-8

  • Клиент должен указывать Accept. Если запрошенный тип не поддержан → 406 Not Acceptable

  • На вход сервер требует корректный Content-Type; неподдерживаемый формат → 415 Unsupported Media Type

Пагинация

  • Стандарт для публичных API: страничная пагинация параметрами: page (>=1), perPage (1..100, по умолчанию 20) Ответ:

  • Для больших списков – курсорная пагинация (рекомендация): limit (1..100), after/before (курсор), стабильная сортировка по ключу

Выбор полей и расширение вложенных

Каждый из атрибутов – опционален и может быть реализован для удоства использования API клиентами.

  • Параметры:

    • fields – whitelist полей → вернуть только нужные поля модели

    • omit – blacklist полей → опустить только указанные поля из модели

    • expand – раскрытие вложенных ресурсов, поддерживает вложенность через точку: expand=tags.category → вернуть вместо ID объект связанного ресурса

  • Приоритет: omit > fields > expand

Идентификаторы и числа

  • Идентификаторы в JSON – строки (во избежание потерь точности в клиентах)

  • Денежные суммы: либо целые в минорных единицах (рекоменд.), либо строки с фиксированной точностью

Даты и время

  • Формат – ISO‑8601. В ответах UTC в Z: YYYY-MM-DDThh:mm:ssZ

  • Если необходимо отдать локальное время – используйте явный смещённый формат ±hh:mm и это должно быть задокументировано для конкретного поля

Коды статусов

Статус

Код

Пояснение

200

OK

Успешный ответ

201

Created

Создание ресурса; указывать Location на новый ресурс

204

No Content

Действие без тела ответа

400

Bad Request

Синтаксическая/структурная ошибка

401

Unauthorized

Отсутствует/невалиден токен. Обязательно WWW-Authenticate

403

Forbidden

Аутентифицирован, но нет прав

404

Not Found

Ресурс не найден

405

Method Not Allowed

Метод не поддержан; обязательно Allow со списком методов

406

Not Acceptable

Тип из Accept не поддержан

409

Conflict

Конфликт состояния, например, дубликат

412

Precondition Failed

Нарушены предусловия (If-Match, If-Unmodified-Since)

413

Content Too Large

Превышен размер тела запроса

415

Unsupported Media Type

Неподдерживаемый Content-Type/Content-Encoding

422

Unprocessable Content

Валидация не пройдена, бизнес‑ошибки ввода

428

Precondition Required

Сервер требует предусловий (например, If-Match)

429

Too Many Requests

Превышен rate limit; указывать Retry-After

451

Unavailable For Legal

Метод или API недоступно для текущего региона/локации

500–504

Ошибки сервера/шлюза

Ошибки: Problem Details (RFC 9457/7807)

  • Формат содержимого: application/problem+json.

  • Базовая схема ответа:

  • Поля errors[].pointer – JSON Pointer до поля тела; errors[].parameter – имя query‑параметра

  • Для 401 добавляем WWW-Authenticate

  • Для 405 – Allow

  • Для 429 – Retry-After

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

  • Клиент явно отправляет версию в каждом запросе Заголовок: API-Version: YYYY-MM-DD

  • Альтернатива (по проектному решению): через Accept-тип, например: Accept: application/vnd.planfact.v2025-08-11+json

  • CHANGELOG фиксирует только ломающие изменения

Кэширование и конкурентный доступ

  • Возвращаем валидаторы: ETag (сильный/слабый) и/или Last-Modified

  • Клиент использует If-None-Match/If-Modified-Since для GET; ответ 304 Not Modified

  • Для защиты от потерянных обновлений клиент отправляет If-Match при PUT/PATCH/DELETE; при расхождении → 412

  • Vary проставляем минимально необходимый (обычно Accept, иногда Accept-Language, Accept-Encoding) Не используем глобально Vary: Authorization, Cookie – это раздувает ключи кэша; приватные ответы помечаем Cache-Control: private

Сжатие

  • Сервер поддерживает компрессию ответов согласно Accept-Encoding (обычно gzip, br)

  • Если клиент отправляет сжатое тело, он обязан указать Content-Encoding. При неподдерживаемом кодировании → 415 (и можно вернуть Accept-Encoding со списком поддерживаемых кодировок).

Ограничение запросов (рейт-лимит)

  • Рекомендуемые заголовки семейства RateLimit (IETF draft):

    • RateLimit-Limit: 100

    • RateLimit-Remaining: 42

    • RateLimit-Reset: 1733923200 (unix ts, секунды)

  • При превышении → 429 + Retry-After (секунды или дата RFC‑1123)

CORS

  • Разрешаем домены партнёров/продукта

  • Preflight (OPTIONS) должен возвращать: Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers, Access-Control-Max-Age, при необходимости Access-Control-Allow-Credentials

  • Если Allow-Credentials: true, значение Allow-Origin не может быть *

Безопасность и транспорт

  • Все запросы – только через HTTPS. Небезопасные запросы → 403 с Problem Details.

  • Аутентификация: Authorization: Bearer <token>

  • В каждый ответ включаем Request-Id (корреляция логов)

Требования к ответам

  • JSON «pretty printed» только в dev/staging (в prod – компактно)

  • Поля и их типы должны быть стабильны; новые поля – обратно‑совместимые

Требования к документации

  • Для каждого метода: URI, параметры, схема запроса/ответа, коды статусов, примеры ошибок (Problem Details)

  • Поддерживаем живую спецификацию OpenAPI (JSON/YAML) как «источник истины»

Последнее обновление