Билол Саидумаров
Все статьи

7 решений в API, которые окупаются годами

15 апреля 2025 · 5 минут · API, архитектура, дизайн
Если коротко
  • Семь решений ценой в день. Возврат — годы спокойной интеграции.
  • Идемпотентность и request-ID — самые дешёвые. Версионирование и аудит — самые благодарные.
  • Плюс два решения, которые я бы сделал иначе, если бы пересобирал сейчас.
Изображение · hero · 1600×900
Дом, в котором аккуратно разведена проводка. Семь розеток на стене подсвечены — каждая со своей функцией. Снаружи — мирно идущий дождь.
Minimalist editorial illustration of a tidy interior wall with seven distinct sockets/switches in a neat row, each glowing a different soft shade of indigo. Through a window, a soft rain falls calmly outside. Calm violet-to-indigo palette, no logos, no readable text, 16:9, 1600x900, professional editorial style.

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

Ниже — семь решений, которые мы закладывали в шлюз с первой недели. И два, которые я бы сегодня сделал иначе.

1. На тяжёлую операцию — 202, а не 201

Загрузка файла запускает OCR и эмбеддинг. Это секунды, иногда десятки. Если клиент сидит на открытом соединении — он первый умрёт от таймаута, а вы — вторым, от чужих ретраев.

Шлюз отвечает 202, отдаёт ссылку на ресурс задачи и текущий статус. Клиент сам опрашивает — раз в полторы секунды, через простой HTMX-polling. Никаких висящих коннектов, никаких героических WebSocket'ов в первой версии.

2. Idempotency-Key — пять строк, спасающих класс багов

Заголовок Idempotency-Key. Один и тот же ключ — один и тот же ответ, сколько бы раз клиент ни повторил запрос.

Сеть всегда подведёт. Клиент всегда переретраит. Без идемпотентности это означает три счёт-фактуры там, где должна была быть одна.

3. Backpressure через 429 + Retry-After

Очередь раздулась — шлюз отвечает 429 с заголовком Retry-After: 5. Это не отказ. Это контракт: «мы устали, вернись через пять секунд». Клиент видит и не давит.

Альтернатива — копить запросы в памяти и упасть всем вместе. Дешевле признаваться в усталости.

4. Разные двери для человека и для машины

Дашборд — HTTP Basic / сессия. Программные интеграции — X-API-Key. Не пытайтесь скрестить: получится не велосипед, а гибрид с тремя колёсами.

Два дерева ответственности: одно про живого пользователя (с MFA, ролями, сроками сессии), другое про машину (с ротацией ключей, скоупом, лимитами per-key).

5. Версия — в пути, не в заголовке

Когда настанет момент v2, путь будет /v2/documents, а не Accept-Version: 2.

Видно в curl, видно в логах, видно в браузере. v1 живёт рядом, обслуживается параллельно, на неё можно сослаться без танцев с заголовками.

6. Аудит-журнал с первого дня

Каждое модифицирующее действие пишет строку в append-only таблицу: actor, action, resource, request_id, details. Не «добавим, когда придёт проверка», а сразу.

Журнал, написанный задним числом, никогда не догоняет реальную историю. Подробнее — в отдельной заметке.

7. Один X-Request-ID на весь путь

Клиент прислал ID — используем. Не прислал — генерируем. Идёт через очередь во все воркеры. Каждая лог-строка несёт его рядом с сообщением.

Через полгода поддержка превращается из «давайте посмотрим логи, может найдём» в «вот ID, держите всю трассу запроса». Это разница между «час разбираться» и «минута ответить».

Что бы я сделал иначе

Лимиты per-key, а не глобальные. Сейчас все клиенты делят одну норму. На демо это норм, в зрелой системе — нет: один шумный клиент уносит остальных. Тарифные тиры стоят дешевле, когда их закладывают сразу.

OpenAPI как первоклассный артефакт, а не побочный продукт FastAPI. С примерами, версионированием, генерируемыми SDK. Писать интеграцию без человеческой OpenAPI — это тренировка терпения, а не работы.

Что сделать дальше
  • Откройте свой API и поставьте крестики напротив семи пунктов. Считайте честно.
  • Начните с двух самых дешёвых — Idempotency-Key и X-Request-ID. Час работы, годы экономии.
  • Посмотрите OpenAPI в демо ARA на /docs.