· 9 мин

API от А до Я: теория и практика

API-ужасы из банковской разработки, сравнение протоколов и ошибки, которые я видел в каждой второй спецификации. Материал курса по системному анализу.

Спецификация, которая чуть не убила интеграцию

Осень 2021-го. Новый проект — и в первые же дни мне дают на ревью API-спецификацию от смежной команды. Открываю Swagger-файл. Все эндпоинты — POST. Все. Включая получение данных клиента, включая проверку статуса, включая удаление. POST /api/client/get, POST /api/client/delete, POST /api/client/getStatus. Статус-коды: 200 на всё, включая ошибки — ошибка кладётся в body как JSON-поле "error": "not found". Версионирования нет. Пагинации нет. На вопрос «а где документация по полям?» — ссылка на Excel-файл в шаре, датированный прошлым годом.

Я не преувеличиваю. Это не худший API, который я видел в enterprise. Это средний. И когда я начал преподавать курс по системному анализу, я понял почему — людей просто не учат проектировать API. Учат слово «REST», но не объясняют, что за ним стоит.

Протоколы: таблица вместо тысячи слов

Прежде чем разбирать ошибки — карта территории. Потому что «API» — это не только REST, и выбор протокола определяет всё остальное.

Сравнение API-протоколов

КритерийRESTSOAPGraphQLJSON-RPCgRPC
Формат данныхJSON (обычно)XML (строго)JSONJSONProtobuf (бинарный)
ТранспортHTTPHTTP, SMTP, JMSHTTPHTTP, WebSocketHTTP/2
КонтрактOpenAPI/SwaggerWSDLSchema + SDLJSON Schema.proto файлы
ТипизацияСлабаяСтрогаяСтрогаяСлабаяСтрогая
КешированиеHTTP-кеш из коробкиНетСложно (один URL)НетНет
СтримингSSE, WebSocket отдельноНетSubscriptionsНетBidirectional
Порог входаНизкийВысокийСреднийНизкийСредний
Где живётВездеEnterprise, госы, банкиФронтенд-тяжёлые приложенияВнутренние вызовыМежсервисное взаимодействие

В банке ты встретишь всё. Один интеграционный слой может общаться с SOAP-сервисами ядра АБС, REST API для мобильного приложения, gRPC между собственными микросервисами и JSON-RPC для внутренних утилит. Идея «давайте всё переведём на REST» разбивается о реальность: ядро АБС переписывать никто не будет.

REST: что на самом деле имел в виду Филдинг

REST — не протокол. Это архитектурный стиль. И большинство «REST API» в продакшене — не REST, а HTTP API с JSON. Разница принципиальна.

Настоящий REST подразумевает statelessness (сервер не хранит сессию), единообразный интерфейс через HTTP-методы, адресацию ресурсов через URI, и HATEOAS (гипермедиа как движок состояния приложения). Последний пункт не реализует почти никто, и это нормально — для большинства задач он избыточен.

HTTP-методы — вот где начинается веселье. GET — получить, идемпотентный. POST — создать, не идемпотентный. PUT — заменить целиком, идемпотентный. PATCH — обновить частично. DELETE — удалить, идемпотентный. Каждый несёт семантику, и эта семантика — не декорация. Прокси, кеши, мониторинг — все полагаются на правильное использование методов. Когда ты делаешь POST /getUser, ты ломаешь не только эстетику, ты ломаешь инфраструктуру.

Статус-коды — вторая боевая зона. 200 (ок), 201 (создано), 204 (ок, без тела), 400 (клиент ошибся), 401 (не авторизован), 403 (нет прав), 404 (не найдено), 409 (конфликт), 500 (сервер умер). Возвращать 200 на ошибку с описанием в body — значит превратить HTTP в тупой транспорт и убить всю семантическую инфраструктуру, которую тебе дали бесплатно.

SOAP: динозавр, который переживёт нас всех

В стартапах SOAP — мем. В банке — суровая реальность. WSDL-описание контракта, envelope-header-body структура, WS-Security для шифрования и подписи, WS-ReliableMessaging для гарантированной доставки. Строгая типизация через XSD-схемы.

Я работал с интеграциями, где SOAP-сервис ядра АБС не менялся годами — и все новые системы были обязаны подстроиться. Менять сервис, обслуживающий миллионы транзакций в день, потому что «REST моднее» — идея, от которой бизнес справедливо шарахается.

Когда выбирать SOAP: строгая типизация контрактов критична, нужна транзакционность и гарантия доставки, интеграция идёт с системами, которые говорят только на SOAP. В банке третий пункт перекрывает все остальные.

GraphQL и JSON-RPC: нишевые, но полезные

GraphQL решает реальную проблему: мобильному клиенту нужны три поля из объекта, а REST возвращает пятьдесят. Один endpoint, клиент контролирует структуру ответа, встроенная типизация через schema. Но сложность переезжает на сервер, кеширование через одинаковые POST-запросы — боль, а глубоко вложенные запросы могут положить базу.

JSON-RPC — когда взаимодействие описывается как «вызвать функцию с параметрами», а не «операция над ресурсом». Внутренние сервисные вызовы, утилиты, всё, что не ложится на ресурсную модель REST.

gRPC: когда миллисекунды решают

gRPC стоит в таблице, но заслуживает отдельного абзаца — потому что в микросервисной архитектуре он всё чаще становится стандартом де-факто. Protobuf вместо JSON — бинарная сериализация, строгая типизация через .proto-файлы, HTTP/2 с мультиплексированием и двунаправленным стримингом. На нашем банковском проекте (подробнее о realtime-архитектуре) gRPC использовался для связи между scoring-сервисом и движком решений: там, где REST давал 15-20 мс на вызов, gRPC укладывался в 2-3 мс. При тысячах вызовов в секунду разница между «работает» и «не укладывается в SLA».

Цена входа — генерация клиентов и серверов из .proto, отсутствие человекочитаемого формата (забудь про curl для дебага), и необходимость gRPC-gateway, если нужен доступ из браузера. Но для межсервисного взаимодействия внутри периметра — лучшего варианта я пока не встречал.

Как я выбираю протокол

Четыре вопроса, которые экономят недели споров:

Кто потребитель? Внешний партнёр — REST, потому что его поймут все. Внутренний сервис с жёсткими требованиями к контракту — SOAP или gRPC. Мобилка с десятком разных экранов — GraphQL.

Что диктует ландшафт? Если корпоративный ESB работает на SOAP, добавлять GraphQL ради одного сервиса — безумие. Если все микросервисы на gRPC — REST-сервис будет выглядеть как чужак.

Кто будет поддерживать? REST — проще найти людей. GraphQL — нужна экспертиза. SOAP — нужны люди, которые не боятся XML и умеют читать XSD.

Какие требования к надёжности? Нужна транзакционность — SOAP. Допустимы retry и eventual consistency — REST.

Однажды эти четыре вопроса спасли меня от месяца бессмысленной работы. Команда фронтенда пришла с запросом: «Нам нужен GraphQL, мы устали тянуть лишние поля». Я задал первый вопрос — кто потребитель? Оказалось, у сервиса ровно два клиента: мобилка и внутренний бэк-офис. Оба потребляли по три эндпоинта. Выигрыш от GraphQL — ноль, а стоимость поддержки schema, резолверов и N+1-проблем — вполне реальная. Мы добавили два query-параметра для выбора полей в существующий REST и закрыли вопрос за день. GraphQL — отличная технология, но для двух потребителей с тремя запросами это как арендовать фуру, чтобы отвезти пакет молока из магазина.

Пять API-ошибок, которые я встречаю в каждом проекте

1. Глаголы в URL

/getUsers, /createOrder, /deleteAccount. HTTP-метод уже содержит глагол. GET /users = «получить пользователей». POST /orders = «создать заказ». Когда я вижу POST /createUser — я знаю, что проектировщик скопировал URL из какого-то туториала 2010 года.

В нашем проекте мы ловили это на ревью. Один из разработчиков сделал POST /calculateRisk — и формально это не ресурсная операция, это процедура. Мы долго спорили. В итоге решили: POST /risk-assessments — создаёшь объект «оценка риска», и это ложится на ресурсную модель. Но спор был полезный — он показал, что REST не для всех задач.

2. Всё через POST

Та самая спецификация из начала статьи. POST на чтение, POST на удаление, POST на всё. Почему так делают? Потому что «POST точно работает через все прокси» и «не хочу разбираться в идемпотентности». Ленивое решение, которое аукается на каждом следующем этапе: нельзя кешировать GET-запросы, нельзя безопасно ретраить, нельзя отличить чтение от записи в мониторинге.

3. 200 OK на ошибки

{
  "status": 200,
  "error": true,
  "message": "User not found"
}

Я видел это в продакшене крупного банка. API Gateway честно проксирует ответ, мониторинг видит 200, алертов нет. А клиент не получает данные. Мы неделю искали, почему у аналитической витрины пропадают данные — оказалось, upstream-сервис возвращает 200 с ошибкой в body, и наш ETL-процесс считает это успехом.

4. Нет версионирования

/api/users вместо /api/v1/users. Первые полгода всё хорошо. Потом бизнес просит добавить поле, которое ломает обратную совместимость. И начинается: либо ломаем всех потребителей разом, либо вводим хаки с optional-полями и conditions в документации. В банковской системе, где к одному API могут ходить десятки потребителей — это не теоретическая проблема, а инцидент на проде.

5. Нет пагинации

Endpoint, который возвращает все записи. На тестовом стенде их сто — работает. В продакшене — миллион. Один неудачный запрос — и сервис лежит под OOM. У нас это случилось с эндпоинтом выгрузки операций клиента. Разработчик добавлял его «на потом, пагинацию прикрутим позже». «Позже» наступило в виде инцидента в три часа ночи.

Бонус: практика на Telegram-боте

В курсе по системному анализу я даю задание: построить Telegram-бота на Flask. Telegram Bot API — отлично документированный REST API. Flask принимает webhook (POST с JSON), парсит сообщение, обрабатывает команду, отправляет ответ через api.telegram.org.

Один проект — и студенты на практике осваивают HTTP-методы, JSON-сериализацию, вебхуки, аутентификацию через токен, обработку ошибок. Теория API перестаёт быть абстракцией, потому что бот либо работает, либо нет. Никакого «ну, в принципе правильно».

Зачем тебе это знать

У меня был джуниор-аналитик, который пришёл согласовывать интеграцию с внешним партнёром. Партнёр прислал Swagger-спецификацию. Парень открыл файл, посмотрел на стену эндпоинтов и честно сказал: «Я не понимаю, что здесь написано». Не потому что глупый — он был толковый. Просто никто никогда не объяснял ему, что такое path parameter, чем отличается 201 от 200, зачем нужен Bearer-токен в заголовке и почему поле nullable: true в схеме ответа — это не мелочь, а архитектурное решение.

Он провёл встречу с партнёром и согласовал интеграцию «на словах». Без вопросов про формат дат, без уточнений по пагинации, без обсуждения поведения при таймаутах. Через два месяца, когда дошло до разработки, выяснилось, что половина договорённостей не совпадает с тем, что написано в спеке. Ещё месяц на пересогласование. Классика.

Если ты аналитик — умение читать API-спецификацию не опционально. Это как умение читать чертежи для инженера. Без этого ты не проектируешь интеграцию, а играешь в испорченный телефон между бизнесом и разработкой.


Оригинал статьи: API от А до Я на Habr

Ваш ДПУПП

Подписка на обновления

Новые статьи, доклады и проекты — без спама, только по делу.

Без спама. Отписка в один клик.

Похожие статьи