Дизайн HTTP API
Используемые стандарты
IETF drafts: Idempotency‑Key header; RateLimit headers
Ресурсы и 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+jsonCHANGELOG фиксирует только ломающие изменения
Кэширование и конкурентный доступ
Возвращаем валидаторы:
ETag(сильный/слабый) и/илиLast-ModifiedКлиент использует
If-None-Match/If-Modified-Sinceдля GET; ответ 304 Not ModifiedДля защиты от потерянных обновлений клиент отправляет
If-MatchприPUT/PATCH/DELETE; при расхождении → 412Vary проставляем минимально необходимый (обычно
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: 100RateLimit-Remaining: 42RateLimit-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) как «источник истины»
Последнее обновление