Начать проект

Обсудим проект?

Нажимая на кнопку, вы даете согласие на обработку персональных данных и соглашаетесь с политикой конфиденциальности.

Отправлено!

Спасибо за заявку, мы свяжемся с вами в ближайшее время!

Проектирование API: как выбрать стиль, договориться о контракте и не переделывать через год

Проектирование API — контракт между сервисами, версии и документация

Симптом плохо спроектированного API всегда один: каждая новая интеграция превращается в проект. Партнёр не может подключиться без вашего разработчика, мобильное приложение требует переделки серверной части, а на вопрос «что вернётся в этом поле» никто не отвечает без похода в код. Мы в Code Pilots делаем продукты с интеграциями и чаще всего приходим именно в такую ситуацию.

Если совсем коротко. API — это контракт, а не набор ссылок: его проектируют до реализации, потому что менять его после подключения потребителей дорого. Рабочий дефолт для внешних потребителей — REST с описанием в OpenAPI; GraphQL берут, когда фронтенду нужны гибкие запросы по вложенным данным; gRPC — для внутреннего обмена между своими сервисами.

Четыре вещи определяют, переживёт ли API вторую интеграцию: идемпотентность мутирующих запросов, обрабатываемый формат ошибок, продуманное версионирование и актуальная документация. Всё остальное — детали.

Зачем проектировать API отдельно

Кажется, что API появляется сам: написали серверную часть, открыли несколько адресов — готово. Проблемы начинаются на втором потребителе.

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

Каждое неудачное решение умножается. Странное имя поля, невнятный код ответа, отсутствие идентификатора запроса в ошибке — всё это придётся объяснять каждому новому потребителю, и объяснять будут ваши разработчики вместо работы.

API определяет скорость продуктовых изменений. Если серверная часть отдаёт данные ровно в том виде, в котором их показывает текущий интерфейс, любая переделка экрана превращается в переделку сервера.

Практический пример из наших проектов: у PetShop мобильное приложение вышло за две недели именно потому, что серверная часть была спроектирована как API-платформа, а не как приложение с прикрученными адресами. Как устроена серверная часть в целом, разобрано в материале про бэкенд.

Три стиля: REST, GraphQL, gRPC

Универсального ответа нет, но есть работающее правило выбора по типу потребителя.

Стиль Когда подходит Плюсы Чем платите
REST Публичные и партнёрские API, интеграции, мобильные приложения Понятен всем, кешируется, отлично документируется в OpenAPI, работает без клиентских библиотек Много запросов на сложных экранах, лишние поля в ответах
GraphQL Фронтенд с гибкими запросами по вложенным данным, несколько разных клиентов Клиент берёт ровно нужные поля, одна точка входа Сложнее кеширование, лимиты сложности запросов, выше порог входа для партнёров
gRPC Внутренний обмен между своими сервисами, высокая частота вызовов Быстрый бинарный протокол, строгий контракт, кодогенерация Не работает напрямую из браузера, тяжелее отлаживать, нужен контроль над обоими концами

Практика 2026 года: REST остаётся безопасным дефолтом для всего, что смотрит наружу, gRPC живёт внутри между сервисами, а GraphQL чаще всего появляется в роли агрегатора над ними — шаблон backend-for-frontend, который стал доминирующей корпоративной моделью.

Отдельно про мобильные приложения: им обычно нужны крупные экономные ответы под конкретный экран, а не десять запросов на отрисовку списка. Это решается либо агрегирующими методами в REST, либо GraphQL-слоем; как устроена клиентская часть, которая всё это потребляет, — в материале про архитектуру мобильного приложения.

REST, GraphQL и gRPC: какой стиль для какого потребителя

Ресурсы, методы и коды ответов

Скучная часть, на которой экономят время, а потом платят поддержкой.

Ресурсы — существительные, действия — методы. `/orders`, `/orders/{id}`, `/orders/{id}/items`, а не `/getOrderList` и `/createNewOrder2`. Это не эстетика: предсказуемая схема позволяет партнёру угадывать адреса, не открывая документацию.

Коды ответов по назначению. 200 — успех, 201 — создано, 400 — ошибка в запросе, 401 — не аутентифицирован, 403 — нет прав, 404 — нет объекта, 409 — конфликт состояния, 422 — не прошла валидация, 429 — превышен лимит, 5xx — проблема на вашей стороне. Ответ «200 OK» с телом `{"error": "not found"}` ломает любую типовую обработку на клиенте.

Единый стиль имён. Один регистр, одинаковые названия для одних и тех же сущностей во всех методах, единый формат даты — ISO 8601 с часовым поясом. Смешанные `user_id` и `userId` в одном API стоят каждому интегратору по часу на выяснение.

Явные единицы измерения. `amount` в копейках или в рублях, `weight` в граммах или килограммах — пишется в документации и, желательно, в имени поля.

Идемпотентность: почему это обязательно

Самое дорогое, что можно пропустить. Сеть ненадёжна: клиент отправил запрос на создание заказа, ответ не дошёл из-за таймаута, клиент повторил. Без защиты у вас два заказа и два платежа.

Рабочее решение — ключ идемпотентности: клиент генерирует уникальный идентификатор операции (обычно UUID) и передаёт его в заголовке. Сервер запоминает результат по этому ключу и на повтор отдаёт тот же ответ, а не создаёт новую сущность. Именно так работает Stripe, и это фактический стандарт для платежных и заказных API.

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

Практическое следствие для проекта: если сейчас идемпотентности нет, добавить её позже сложнее, чем кажется — нужно хранить результаты операций и договариваться с потребителями о новом заголовке. Дешевле заложить сразу.

Повтор запроса при таймауте не должен создавать второй заказ — это требование, а не тонкость.

Ошибки, которые можно обработать

Ошибка — это тоже часть контракта, и её формат проектируют так же, как успешный ответ. Минимальный набор в теле ответа:

  • Машинный тип ошибки. Строковый код вроде `insufficient_stock`, по которому клиент строит логику. Не текст сообщения — тексты меняются.
  • Человеческое сообщение. Что случилось, на языке пользователя, если ошибку планируют показывать.
  • Детали по полям. Для валидации: какое поле не прошло и почему.
  • Идентификатор запроса. Одна строка, по которой ваша поддержка находит запрос в логах. Экономит часы на каждом обращении.

Отдельное правило: не отдавайте внутренние подробности в ошибках наружу. Трассировка стека и тексты запросов к базе — это и утечка информации, и бесполезный для клиента шум. Внутрь — полный лог с идентификатором, наружу — короткий понятный ответ.

Ответ «200 OK» с текстом ошибки внутри ломает любую типовую обработку на стороне клиента.

Версионирование и обратная совместимость

Версия нужна не потому, что так принято, а потому что вы не можете обновить чужие клиенты по своему желанию.

Изменение Ломает совместимость Что делать
Добавили новое поле в ответ Нет Выпускать в текущей версии
Добавили необязательный параметр запроса Нет Выпускать в текущей версии
Добавили новый метод Нет Выпускать в текущей версии
Переименовали или удалили поле Да Новая версия или поле-синоним на переходный период
Поменяли тип значения Да Новая версия; молча менять нельзя
Поменяли смысл значения при том же типе Да, и это худший случай Новое поле с новым именем
Сделали параметр обязательным Да Новая версия
Поменяли код ответа в существующем сценарии Да Новая версия

Механизмы. Версия в пути (`/v1/orders`) — операционно самый простой: видно в логах, в документации и в разговоре с партнёром. Заголовок с версией — гибче, так делает GitHub. Версии по дате с обратной трансформацией ответов — подход Stripe, самый дружелюбный к потребителю и самый дорогой в поддержке. Механизм важнее написания: главное, чтобы старые клиенты продолжали работать.

Срок жизни версии. Объявляйте заранее, сколько поддерживаете предыдущую и как уведомляете о выключении. Партнёру нужно время на доработку, а вам — понятная точка, после которой можно удалить старый код.

Спроектируем API, который переживёт вторую интеграцию

Контракт, версии, формат ошибок и идемпотентность — до первой строки кода, а не после жалоб партнёра.

Обсудить проект

Пагинация и большие выборки

Место, где API начинает падать на реальных объёмах.

Пагинация обязательна с первого дня. Метод, который отдаёт «все заказы», однажды получит клиента с миллионом записей и уронит и себя, и базу.

Смещение против курсора. Пагинация по смещению проста, но на больших объёмах медленная и даёт дубли при вставках между запросами. Курсорная — сложнее в реализации, стабильна и быстра; для растущих данных это правильный выбор.

Лимиты. Максимальный размер страницы задаётся на сервере, а не доверяется клиенту. Иначе первый же запрос с лимитом в сто тысяч записей станет инцидентом.

Тяжёлые выгрузки — отдельным механизмом. Полная синхронизация каталога или истории заказов делается не через обычный метод списка, а через асинхронную задачу: клиент запрашивает выгрузку, получает идентификатор, потом забирает готовый файл. Так тяжёлая операция не занимает соединение и не мешает остальным.

Аутентификация, права и лимиты

Три разные задачи, которые часто путают.

Аутентификация. Кто вызывает: API-ключ для сервер-сервер интеграций, токены OAuth для доступа от имени пользователя, подпись запроса для чувствительных операций. Ключи должны быть отзываемыми и с разными правами для тестового и рабочего контура.

Авторизация. Что именно этому потребителю разрешено. Партнёр видит свои заказы, а не все; интеграция склада читает остатки, но не меняет цены. Права проверяются на сервере по каждому объекту, а не только по методу.

Лимиты. Ограничение частоты запросов защищает вас от чужой ошибки в цикле. Лимит должен возвращать код 429 и заголовок с временем, через которое можно повторить, — тогда нормальный клиент подстроится сам.

Плюс базовая гигиена: только защищённое соединение, никаких ключей в адресе запроса (они попадают в логи), разные ключи на каждого потребителя, чтобы можно было отключить одного, не сломав остальных.

Контракт первым: OpenAPI и документация

Подход, который экономит больше всего времени на проектах с несколькими командами: сначала пишется и согласуется контракт, потом код.

Контракт в формате OpenAPI — это машиночитаемое описание методов, параметров, схем данных и ошибок. Версия 3.1 полностью выровнена с JSON Schema, а обновление 3.2 добавило структурированную навигацию по тегам и стриминговые типы данных.

Что даёт контракт-первый подход на практике: фронтенд и мобильная команда начинают работу до готовности серверной части, работая с заглушками по контракту; документация и клиентские библиотеки генерируются из того же файла, а не пишутся отдельно и не устаревают; изменения контракта видны в код-ревью как обычный диф; несоответствие реализации контракту ловится автоматическими проверками.

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

Contract-first: из одного файла OpenAPI получаются документация, SDK и проверки

Разберём ваше API по контракту

Посмотрим, что мешает партнёрам подключаться без вашего разработчика, и оценим, что можно исправить доработками.

Получить консультацию

Тестирование и наблюдаемость

Три уровня, каждый закрывает свой класс проблем.

Контрактные тесты. Проверяют, что реализация соответствует OpenAPI: те же поля, типы, коды ответов. Ловят самый частый класс поломок — «поле переименовали, забыли предупредить».

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

Наблюдаемость в бою. Метрики по методам: частота, время ответа по перцентилям, доля ошибок 4xx и 5xx. Логи с идентификатором запроса. Алерт на рост доли ошибок у конкретного потребителя — часто это единственный способ узнать, что партнёр обновился и что-то сломал.

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

Внутреннее, партнёрское и публичное API

Требования отличаются радикально, и это стоит решить до проектирования.

Тип Кто потребитель Что обязательно Что можно упростить
Внутреннее Свои сервисы и приложения Стабильность контракта внутри релиза, метрики Публичная документация, SDK, длинная поддержка версий
Партнёрское Ограниченный круг известных интеграторов Документация, версии со сроком жизни, тестовый контур, разные ключи Самообслуживание, публичная песочница
Публичное Любой разработчик Полная документация, самостоятельная регистрация, песочница, лимиты, SDK, канал поддержки

Частая ошибка — объявить внутреннее API партнёрским, не добавив ни документации, ни тестового контура. Каждый новый партнёр тогда подключается через переписку с вашим разработчиком, и это тихо съедает недели инженерного времени в год. В корпоративном контуре, где потребителей и систем много, счёт идёт уже на человеко-месяцы: про этот класс систем есть отдельный материал про enterprise-системы.

Отдельная тема — обмен большими объёмами и события. Если партнёрам нужны уведомления об изменениях, вебхуки дешевле, чем опрос вашего API по расписанию; а для сложного обмена между многими системами обычно правильнее не API-к-API, а управляемый слой обмена, как в материале про интеграционную шину.

API — это продукт, у которого пользователи разработчики: документация для него не приложение, а интерфейс.

Вебхуки и события

Обратная сторона API: иногда данные должны идти от вас к потребителю, а не наоборот. Если партнёр раз в минуту опрашивает ваш метод «есть новые заказы?», вы платите за это нагрузкой, а он — задержкой.

Что отправлять. Событие с типом, идентификатором объекта и временем. Полное состояние объекта в теле — удобно, но означает, что вы отдаёте данные наружу без запроса; иногда правильнее отдать только идентификатор, а детали потребитель забирает сам.

Подпись и проверка. Вебхук должен быть подписан секретом, чтобы получатель убедился, что запрос от вас. Без подписи любой может прислать «оплата прошла».

Повторы и порядок. Получатель будет недоступен — это норма. Нужны повторы с растущими интервалами, ограничение числа попыток и журнал доставки. Порядок событий не гарантируется, поэтому у каждого события должны быть номер и время, а обработчик на стороне партнёра должен быть готов к дублям.

Ответ получателя. Договоритесь, что считается успешной доставкой: код 200 и ничего больше. Ждать от получателя бизнес-результата в ответе на вебхук — плохая идея: он начнёт делать в обработчике тяжёлую работу и упираться в таймаут.

Чем это лучше опроса. Меньше пустых запросов, меньше задержка, ниже нагрузка на обе стороны. Но появляется новая обязанность: следить за доставкой и давать партнёру страницу с историей отправок, иначе разбор «мы не получили событие» превращается в переписку.

Если партнёр каждую минуту спрашивает «есть новые заказы?», вы платите нагрузкой, а он — задержкой.

Сколько стоит и от чего зависит

Стоимость определяют четыре фактора: число сущностей и методов, требования к типу API (внутреннее заметно дешевле публичного), объём миграции существующих потребителей и необходимость поддерживать старые версии.

Ориентиры по нашим работам: отдельная интеграция или проектирование и разработка API под конкретный обмен — от 150 тыс. до 1,5 млн ₽, продукт с API-платформой под процесс — от 1,7 млн ₽, аудит существующего API и архитектуры — от 200 до 600 тыс. ₽. Точная сумма считается по числу методов и потребителей: оценка проекта бесплатная — опишите задачу.

Что почти всегда недооценивают в смете: документацию и тестовый контур (без них API формально есть, а подключиться нельзя), миграцию существующих потребителей на новую версию и поддержку старой версии в течение переходного периода. Прикинуть порядок суммы с учётом этих работ можно в бесплатном калькуляторе оценки.

Ошибки проектирования

Проектировать API под текущий экран. Через год интерфейс меняется, и сервер приходится переписывать вместе с ним.

Отдавать 200 на ошибку. Клиент не может отличить успех от неудачи стандартными средствами, и каждый интегратор пишет свою обработку.

Пропустить идемпотентность. Двойные заказы и платежи всплывают на первом же сетевом сбое, а добавлять защиту потом дороже.

Не заложить пагинацию. Метод «отдать всё» работает до первого крупного клиента.

Оставить документацию на потом. Она не появляется никогда, и её роль начинает исполнять ваш разработчик в переписке.

Ломать совместимость молча. Переименованное поле роняет чужие интеграции в продакшене, и это самый быстрый способ испортить отношения с партнёром.

Если API уже живёт и мешает — начинать стоит не с переписывания, а с диагностики: аудит кода и архитектуры обычно показывает, что достаточно привести к порядку формат ошибок, добавить версионирование и описать контракт, а не строить всё заново.

С чего начать

Три шага до проектирования. Первое — выписать потребителей: свои приложения, партнёры, интеграции с учётными системами, будущее мобильное приложение. Именно список потребителей определяет тип API и требования к нему. Второе — выписать сущности и операции над ними в терминах бизнеса: заказ, товар, остаток, клиент, платёж. Третье — договориться о четырёх сквозных решениях: формат ошибок, идемпотентность, пагинация, версионирование.

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

С чего начать: список потребителей, сущности и операции, четыре сквозных решения

Обсудим ваш проект?

Соберём контракт под ваших потребителей и оценим объём работ. Оценка бесплатная.

Обсудить проект

Часто задаваемые вопросы

По типу потребителя. REST — безопасный дефолт для всего, что смотрит наружу: партнёры, интеграции, мобильные приложения. GraphQL оправдан, когда у вас несколько клиентов и фронтенду нужны гибкие запросы по вложенным данным. gRPC — для внутреннего обмена между своими сервисами с высокой частотой вызовов. Частый гибрид: gRPC внутри, REST наружу, GraphQL как агрегатор для фронтенда.

Чтобы повтор запроса при сетевом сбое не создавал второй заказ или второй платёж. Реализация: клиент передаёт уникальный ключ операции в заголовке, сервер сохраняет результат по этому ключу и на повторный запрос возвращает тот же ответ вместо создания новой сущности. Обязательно для всего, что создаёт документы или списывает деньги.

Так, чтобы старые клиенты продолжали работать. Версия в пути вида `/v1/` операционно самая простая: видно в логах и в разговоре с партнёром. Есть варианты с заголовком версии и с версиями по дате и обратной трансформацией ответов — они дружелюбнее к потребителю, но дороже в поддержке. Главное — заранее объявить срок жизни версии и правило уведомления об отключении.

Подход, при котором контракт API в формате OpenAPI пишется и согласуется до реализации. Он даёт три вещи: фронтенд и мобильная команда начинают работу по заглушкам, не дожидаясь сервера; документация и клиентские библиотеки генерируются из того же файла и не устаревают; расхождение реализации с контрактом ловится автоматическими проверками.

Зависит от числа сущностей и методов, типа API (внутреннее дешевле публичного), объёма миграции существующих потребителей и необходимости поддерживать старые версии. У нас проектирование и разработка API под конкретный обмен — от 150 тыс. до 1,5 млн ₽, продукт с API-платформой — от 1,7 млн ₽, аудит существующего API — от 200 до 600 тыс. ₽. В смету обязательно входят документация и тестовый контур: без них подключиться к API нельзя.

Разработка с Code Pilots

Code Pilots — студия заказной разработки полного цикла: веб- и мобильные продукты с нетривиальной бизнес-логикой, интеграциями и высокими нагрузками. Интеграционная экспертиза — наша сильная сторона: обмены с 1С и хранилищами данных, очереди, отказоустойчивая доставка, мониторинг обменов со статусами и алертами.

Из практики: у PetShop мобильное приложение вышло за две недели, потому что серверная часть была спроектирована как API-платформа; у B2B-дистрибьютора обмен с хранилищем держит отклик меньше полусекунды на RabbitMQ и Go; в проекте «Радиоэлементы» поставщики подключаются к обмену прайсами сами, без участия разработчиков.

Если API уже есть — начнём с аудита контракта и скажем, что можно привести в порядок доработками, а что придётся переделывать. Если API только проектируется — соберём контракт до первой строки кода. Обсудить проект →

Обсудим проект?

Заполните форму или напишите нам
на airmail@code-pilots.ru
Нажимая на кнопку, вы даете согласие на обработку персональных данных и соглашаетесь с политикой конфиденциальности.

Спасибо!

Спасибо за сообщение, мы свяжемся
с вами в ближайшее время!
success