Интерфейс для обмена данными живёт годами, и неудачные решения приходится поддерживать вместе с ним. Переименовать поле или изменить формат ответа после того, как к API подключились сторонние клиенты, стоит дороже, чем спроектировать всё аккуратно с самого начала. Ниже — порядок, в котором мы проектируем интерфейс, и правила, которые экономят время в дальнейшем.
Начинаем со сценариев, а не с таблиц
Самая частая ошибка — сделать по методу на каждую таблицу базы данных. Такой интерфейс повторяет внутреннее устройство системы, и любая перестройка хранения ломает клиентов. Мы начинаем с вопроса, что именно должен сделать потребитель API: оформить заказ, получить список отгрузок за период, отменить бронирование, узнать статус.
Для каждого сценария выписываем: какие данные нужны на входе, что возвращается, что происходит при ошибке и при повторе. Только после этого появляются ресурсы и методы. Проверка простая: если для одного действия клиента нужно сделать пять запросов и собрать данные у себя, интерфейс спроектирован под базу, а не под задачу.
Именование и структура ресурсов
- Одна сущность называется одинаково везде: если это order, то и в адресе, и в поле ответа, и в документации это order, а не deal в одном месте и purchase в другом.
- Адрес описывает ресурс, а действие задаёт метод запроса. Списки — во множественном числе, вложенность — не глубже двух уровней.
- Единый стиль имён полей по всему интерфейсу. Смешение стилей в одном ответе — верный признак того, что методы писали разные люди и не сверялись.
- Один формат даты и времени во всех методах, с указанием зоны. Разные форматы в разных методах — источник ошибок, которые всплывают через месяцы.
- Идентификаторы возвращаем строками: числовой идентификатор в некоторых языках теряет точность при разборе.
Основные правила
| Правило | Почему |
|---|---|
| Предсказуемые имена | Одинаковые сущности называются одинаково везде |
| Понятные коды ответов | Клиент должен отличать «нет данных» от «ошибка сервера» |
| Структурированные ошибки | Код, сообщение и поле — а не просто текст |
| Постраничная выдача | Списки растут, отдать всё разом нельзя |
| Версионирование | Изменения не должны ломать существующих клиентов |
| Идемпотентность | Повтор запроса не создаёт второй заказ |
| Ограничение частоты | Один клиент не должен класть сервис запросами |
| Секреты в заголовках | Адреса запросов попадают в логи и историю браузера |
| Единый формат дат | Разные форматы ломают разбор на стороне клиента |
| Тестовый контур | Интеграцию нельзя отлаживать на боевых данных |
Коды ответов и формат ошибок
Код ответа — первое, на что смотрит клиентский код. Успех — 200 или 201 при создании, 204 когда возвращать нечего. Ошибка в данных запроса — 400, отсутствие авторизации — 401, недостаток прав — 403, несуществующий ресурс — 404, конфликт состояния — 409, превышение лимита частоты — 429, сбой на нашей стороне — 500. Ответ 200 с текстом ошибки внутри тела — распространённая и дорогая ошибка: клиент считает такой запрос успешным и продолжает работать с испорченными данными.
Тело ошибки делаем одинаковым во всех методах: машинный код ошибки, человеческое сообщение, поле, к которому относится проблема, и идентификатор запроса. Последний позволяет по обращению клиента найти конкретный вызов в логах, не выспрашивая подробности. Список кодов ошибок должен быть закрытым и описанным — клиенты строят на нём свою логику.
Пагинация, фильтры и сортировка
Любой список рано или поздно вырастет. Отдавать его целиком нельзя, поэтому пагинация закладывается сразу, даже если сейчас в таблице сотня строк. Постраничная выборка по номеру страницы проста, но на изменяющихся данных пропускает и дублирует записи; выборка по курсору от этого свободна и лучше подходит для больших наборов.
Правила, которых мы держимся: максимальный размер страницы ограничен сервером, а не клиентом; в ответе всегда есть признак наличия следующей страницы; набор допустимых полей для фильтрации и сортировки описан явно, а не собирается из произвольных параметров. Свободная фильтрация по любому полю выглядит удобной ровно до первого запроса, который кладёт базу.
Версионирование и обратная совместимость
Версия указывается явно — в адресе или в заголовке — и не меняется молча. Добавление нового необязательного поля в ответ совместимо; удаление поля, переименование, смена типа и превращение необязательного параметра в обязательный — нет. Такие изменения выпускаются новой версией, а старая живёт объявленный срок.
- Клиент обязан игнорировать неизвестные поля в ответе — это условие фиксируется в документации.
- Об отключении версии предупреждаем заранее и в самом ответе, а не только в рассылке.
- Значения перечислений расширяем осторожно: клиент может не знать нового статуса и должен вести себя предсказуемо.
Идемпотентность и повторные запросы
Сеть теряет ответы. Клиент отправил запрос на создание заказа, ответ не дошёл, клиент повторил — и заказов стало два. Решение стандартное: клиент передаёт ключ идемпотентности, сервер запоминает результат первой обработки и на повтор с тем же ключом возвращает тот же ответ вместо нового действия. Чтение и удаление идемпотентны по своей природе, проблема касается создающих и изменяющих запросов.
Отдельно договариваемся о поведении при таймауте: сколько ждёт клиент, сколько раз повторяет и с какой задержкой. Повтор без нарастающей паузы превращает единичный сбой в лавину запросов.
Авторизация, ключи и ограничения по частоте
Ключ или токен передаётся в заголовке, а не в адресе запроса: адреса попадают в логи веб-сервера, историю браузера и заголовок источника перехода. У каждого потребителя — свой ключ, чтобы его можно было отозвать, не затрагивая остальных, и понимать по логам, кто именно создаёт нагрузку. Права ключа ограничиваем набором операций, которые действительно нужны интеграции.
Ограничение частоты — не столько защита от злоумышленника, сколько защита от чужой ошибки в цикле. В ответ на превышение возвращаем 429 и указываем, когда можно повторить; текущий остаток лимита полезно отдавать в заголовках каждого ответа, чтобы клиент мог притормозить сам.
Документация, вебхуки и тестовый контур
Документация обязательно содержит адреса методов и параметры с примерами запроса и ответа, формат и полный список ошибок, способ авторизации, ограничения по частоте и порядок уведомления об изменениях версии. Примеры должны быть рабочими: их копируют целиком.
Если система шлёт вебхуки, доставка проектируется отдельно. Получатель может ответить ошибкой или не ответить вовсе, поэтому нужны повторные попытки с нарастающей паузой, подпись сообщения для проверки отправителя и уникальный идентификатор события — принимающая сторона обязана уметь обработать один и тот же вызов дважды без последствий.
Тестовый контур с отдельными ключами и данными обязателен: интеграцию нельзя отлаживать на боевых заказах. Готовность интерфейса мы проверяем практикой — даём документацию разработчику, не участвовавшему в проекте, и просим сделать простую интеграцию. Каждое место, где он приходит с вопросом, нужно исправить в интерфейсе или в описании.
Если нужен интерфейс обмена данными между сайтом, учётной системой и мобильным приложением, мы спроектируем его и опишем: разработка информационных систем и интеграций. Начинаем с разбора сценариев обмена.











