Files
docs/architecture/http-api.md
T
ksv741 256d49b045
Conventional commits / commits (pull_request) Successful in 12s
Docs security / security (pull_request) Successful in 7s
Docs verify / verify (pull_request) Successful in 14s
Docs security / security (push) Successful in 7s
Docs verify / verify (push) Successful in 16s
fix(deps): refresh docs security baseline
2026-10-07 15:36:47 +03:00

8.7 KiB
Raw Blame History

HTTP API и ошибки

Источник контракта

Backend генерирует OpenAPI из собственного публичного API. Frontend использует предоставленный OpenAPI snapshot как актуальный вход и локально генерирует типизированный клиент. Backend владеет контрактом, а подготовка и доставка схемы находятся вне frontend. Это разделение принято в ADR 002 и ADR 014.

Backend командой npm run openapi:generate создаёт openapi.json без запуска сервера и не хранит результат в Git. Contract test сравнивает вывод генератора с Nest runtime schema настроенного приложения. Frontend хранит полученный artifact у себя и проверяет соответствие generated types этой копии. CI не скачивает соседние репозитории и не сравнивает их текущие revisions. Обновление входного artifact потребителя выполняется отдельно с review; совместимость развёрнутых версий не доказывается одним зелёным локальным CI.

API имеет prefix /api/v1; Swagger публикуется для локальной разработки по environment policy, а не используется frontend в runtime.

Инвестиционные данные и синхронизация

T-Bank data GET возвращают envelope { data, meta }. data может быть null при missing или неполном финансовом агрегате; partial ledger/candles могут содержать подтверждённые строки. meta разделяет availability, freshness, lastSuccessfulAt, sync, coverage и состояния зависимостей. dataRevision описывает набор данных; пустое завершённое покрытие явно представлено в coverage. Публичной hash-версии состояния нет. Ни одно поле не раскрывает connection revision.

Обычный GET планирует missing/stale зависимости после проверки доступа. GET с sync=observe только читает. POST /api/v1/tbank-sync принимает валидированный screen descriptor и возвращает 202 с handle и нормализованным scope. GET /api/v1/tbank-sync/:id наблюдает разрешённую работу; чужой или прежний handle возвращает 404. Приём работы не означает её успех.

Status возвращает state/errorCode без данных ресурсов и численных progress counters. Клиент перечитывает показанные resources через sync=observe во время работы и после завершения, сохраняя generated query identity. Sync states ресурса: idle, running, failed; отдельного публичного queued нет.

Source failure принятой работы передаётся безопасным meta.sync.errorCode; HTTP transport/access/input errors сохраняют Problem Details. Старые данные при transient failure сохраняют исходное успешное время. Детали поведения — свежесть и полнота.

Envelope несовместим с прежними bare-array ответами: backend/frontend поставляются согласованной парой. Финальный artifact передаётся явно; локальная генерация и CI frontend читают только собственный snapshot.

Validation и identity

Backend преобразует и проверяет входные данные на общей HTTP-границе. Неизвестные свойства запроса удаляются; сам факт их передачи не обещает отказа. Проверка формата не заменяет бизнес-ограничения и проверку доступа к счёту. Аутентификация подтверждает личность пользователя; accountId из URL сам по себе не доказывает доступ к брокерским данным.

Error contract

Общая граница обработки HTTP-ошибок возвращает application/problem+json со status и публичным code. Неожиданный exception возвращает 500 / INTERNAL_SERVER_ERROR, без исходного message/stack. Типичный ответ:

{ "status": 401, "code": "UNAUTHORIZED" }

Для input validation контракт требует 400, VALIDATION_ERROR и errors как массив { pointer, detail }, где pointer адресует поле запроса. Это требование проверяется реальным HTTP-ответом после всех стадий обработки запроса. Глобальная валидация передаёт фильтру структурированные ошибки без исходных значений и DTO, а фильтр формирует JSON Pointer, включая вложенные поля. HTTP suites подтверждают детали ошибок регистрации, банковской карты и синхронизации; OpenAPI отдельно фиксирует форму ответа.

Ответ не включает автоматически все поля RFC (type, title, instance). Добавление обязательного поля является изменением публичного контракта. Клиент выбирает поведение по публичному коду, а не по внутреннему тексту ошибки.

Domain errors и auth

Специальный code определяется сценарием; общее преобразование ошибок и безопасная форма ответа принадлежат HTTP-границе. Auth endpoints используют свои status и cookie operations: Authentication. T-Bank token failure и временная недоступность имеют разные значения: Connection API. Raw upstream/Prisma errors и secrets не передаются frontend.

Наблюдаемость

Backend пишет структурированные JSON-события в stdout через Pino. Обработка HTTP-запроса получает серверный request ID, который возвращается заголовком X-Request-ID; допустимый входящий идентификатор сохраняется отдельно как client request ID. Это позволяет сопоставить публичную ошибку с обработкой запроса без передачи внутреннего exception в ответ API.

Журнал завершения запроса содержит шаблон маршрута, метод, статус, длительность и при наличии публичный error code. События внешней границы добавляют операцию, результат и число попыток. Разрешённые поля ограничены; полные запросы и ответы, token, cookie и Authorization не являются данными журнала. Фильтрация полей и redaction не защищают секрет, вручную включённый в свободный текст сообщения или stack.

Выбор logger и граница ответственности описаны в ADR 008; согласованное поведение — в workspace-спецификации Structured logging. Сборщик, срок хранения журналов и alerting не определяются приложением.