Перейти к основному содержимому

API-конвенции

Зачем нужно

Документ задаёт обязательные правила HTTP API для всех 7 доменов. Любой backend-сервис экосистемы соответствует этим правилам без исключений.

Базовые правила

  • Транспорт: HTTPS.
  • Формат: JSON (UTF-8).
  • Стиль: REST.
  • Префикс: /api/v2/<namespace>/..., где namespace — один из 7 бизнес-доменов или технический namespace platform. Все namespace используют единое мажорное версионирование v2.
  • Имена ресурсов в URL: kebab-case, существительные во множественном числе (/api/v2/lms/courses).
  • Имена полей в JSON: camelCase.
  • Идентификаторы: UUID v4 в виде строки.
  • Время: ISO-8601 в UTC, с суффиксом Z.
  • Деньги: строка с двумя знаками после точки + поле currency (ISO 4217), например {"amount":"1990.00","currency":"RUB"}.

Envelope ответа

Успешный одиночный ответ:

{
"data": { ... },
"meta": { "requestId": "..." }
}

Ответ списка:

{
"data": [ ... ],
"meta": {
"requestId": "...",
"page": 1,
"perPage": 50,
"total": 1234,
"cursor": "..."
}
}

Ответ ошибки:

{
"error": {
"code": "...",
"message": "...",
"details": [ ... ]
},
"meta": { "requestId": "..." }
}

requestId совпадает с заголовком X-Request-Id и используется для корреляции в логах и трассировке.

HTTP-методы

МетодНазначение
GETчтение, идемпотентен
POSTсоздание или операция, не выражаемая через PUT/PATCH
PUTполная замена ресурса (используется редко)
PATCHчастичное обновление
DELETEудаление или soft delete

Коды ответов

КодКогда
200успешное чтение или операция
201ресурс создан, возвращается тело и Location
202операция принята к асинхронной обработке
204успешно, тело пустое
400bad request, валидация
401не аутентифицирован
403нет прав
404ресурс не найден
409конфликт (idempotency, optimistic locking, business conflict)
410gone (deprecated endpoint)
422бизнес-ошибка валидации
429rate limit
500внутренняя ошибка
503сервис недоступен

Каталог ошибок

Все коды ошибок строятся как <domain>.<entity>.<reason> или platform.<reason>. Примеры:

  • platform.validation_failed
  • platform.unauthorized
  • platform.forbidden
  • platform.not_found
  • platform.conflict
  • platform.rate_limited
  • identity.user.email_taken
  • crm.invoice.already_paid
  • lms.enrollment.entitlement_invalid

Каждый домен ведёт каталог своих ошибок в domains/<name>/api-contracts.md.

Реестр endpoints и contract coverage

  • domains/<name>/api-map.md является canonical endpoint registry: каждый endpoint домена фиксируется там один раз с методом, путём, permission/caller и назначением.
  • domains/<name>/api-contracts.md не обязан дублировать полный request/response DTO для каждой строки registry. Он фиксирует общие DTO, envelope, pagination, idempotency, errors и endpoint-specific DTO для неочевидных, интеграционных, публичных или бизнес-критичных команд.
  • Полное покрытие registry достигается через Endpoint coverage table в api-contracts.md: каждая группа endpoints из api-map.md должна ссылаться на DTO-секцию в api-contracts.md или на профильный features/*.md.
  • Если detailed DTO намеренно не расписывается, coverage row должен прямо указывать применяемые общие правила и доменный DTO/feature doc; placeholders вместо источника контракта не допускаются.

Пагинация

Поддерживаются два режима, выбор фиксируется в api-map.md домена:

Page-based

GET /api/v2/lms/courses?page=2&perPage=50

meta.total обязателен.

Cursor-based

GET /api/v2/lms/activity-attempts?cursor=eyJ...&perPage=100

meta.cursor указывает следующий курсор.

perPage по умолчанию 25, максимум 200.

Сортировка и фильтрация

  • Сортировка: ?sort=field или ?sort=-field (минус — desc). Несколько полей через запятую.
  • Фильтрация: ?filter[<field>]=<value> или ?<field>=<value> для простых случаев. Сложные фильтры — POST на /search.

Идемпотентность

Все небезопасные операции (POST, PATCH, DELETE с побочными эффектами вроде платежа) должны принимать заголовок Idempotency-Key. Сервер сохраняет ответ на этот ключ на 24 часа и при повторном запросе возвращает тот же ответ. Подробности в data-baseline.md.

Optimistic concurrency

Для ресурсов с возможным конкурентным изменением — заголовки If-Match: <etag> и ответ ETag. Конфликт — 409 с кодом platform.optimistic_lock_failed.

Заголовки

Обязательные:

  • Authorization: Bearer <access_token> (для защищённых эндпоинтов);
  • X-Request-Id (если не передан — генерируется сервером);
  • X-Actor-Context требуется только для non-personal контекстов ребёнка/семьи/организации/команды/админа; если заголовок отсутствует, сервер трактует запрос как personal context (описано в auth-integration.md);
  • Idempotency-Key для небезопасных операций;
  • Accept-Language для локали ответа;
  • If-Match / ETag где применимо;
  • Content-Type: application/json для тел.

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

  • Мажорная версия — в URL (v2).
  • Поломочные изменения — новая мажорная версия.
  • Аддитивные изменения внутри мажорной версии разрешены без флагов.
  • Удаление поля = поломочное изменение.

Депрекация: 6 месяцев минимум, эндпоинт возвращает заголовок Deprecation: true и Sunset: <date>. После сроков — 410.

Rate limiting

  • Глобальный лимит на токен и IP описан в security-baseline.md.
  • Локальные лимиты доменов описаны в domains/<name>/security.md.

Health checks

Каждый сервис обязан выставлять:

  • GET /health/live — процесс жив;
  • GET /health/ready — готов принимать трафик;
  • GET /health/info — версия и build info.

OpenAPI

Каждый сервис генерирует и публикует OpenAPI 3.1 схему по адресу /api/v2/<namespace>/openapi.json. Схема собирается из Zod-типов через NestJS интеграцию.

Запрещено

  • Возвращать данные без envelope.
  • Изобретать собственный формат ошибок.
  • Использовать snake_case в JSON.
  • Передавать секреты в query string.
  • Использовать сессионные cookies для public API; cookies используются только для frontend-сессий identity (см. auth-integration.md).
  • Версионировать через заголовок без мажорной версии в URL.

Связанные документы