Содержание
Удобный REST API предсказуем: ресурсы названы существительными, HTTP-методы и коды ответов используются по назначению, ошибки приходят в одном формате, списки листаются одинаково, а изменения не ломают существующих клиентов. Всё это решается соглашениями, принятыми в начале проекта и записанными в спецификацию, а не героизмом при каждой новой ручке.
Ниже — правила, которых мы придерживаемся в API для веб- и мобильных клиентов, с примерами запросов и ответов.
Что делает REST API удобным?
REST API удобен, когда клиент может угадать, как устроен новый эндпоинт, по уже знакомым. Разработчик мобильного приложения не должен каждый раз читать документацию, чтобы понять, как передать фильтр, где лежит ошибка и как получить следующую страницу.
Предсказуемость держится на нескольких соглашениях:
- ресурсы — существительные во множественном числе:
/orders,/orders/42/items; - действие определяет HTTP-метод, а не название пути: не
/getOrders, аGET /orders; - формат дат, денег и идентификаторов один на весь API;
- ошибки, пагинация и фильтры устроены одинаково во всех разделах.
Соглашения стоит записать в короткий документ до первой строки кода и проверять на ревью так же, как стиль кода.
Как называть ресурсы и использовать HTTP-методы?
Ресурсы называют существительными, а HTTP-методы используют по их смыслу: GET читает, POST создаёт, PATCH частично изменяет, PUT заменяет целиком, DELETE удаляет. Вложенность в пути отражает принадлежность, но не глубже одного-двух уровней.
| Операция | Запрос | Успешный ответ |
|---|---|---|
| Список заказов | GET /orders?status=paid&page=2 |
200 и массив с метаданными страницы |
| Один заказ | GET /orders/42 |
200 и объект заказа |
| Создать заказ | POST /orders |
201 и созданный объект |
| Изменить статус | PATCH /orders/42 |
200 и обновлённый объект |
| Удалить заказ | DELETE /orders/42 |
204 без тела |
| Действие над заказом | POST /orders/42/cancel |
200 или 202, если обработка асинхронная |
Последняя строка — исключение из правила «никаких глаголов». Действия, которые не сводятся к изменению поля, — отмена, повторная отправка, оплата — удобнее оформлять отдельным подресурсом, чем прятать в PATCH со специальным значением.
Как возвращать ошибки?
Ошибки возвращают с правильным HTTP-кодом и телом одного формата для всего API: машиночитаемый код, человеческое сообщение и детали по полям. Клиент должен уметь обработать любую ошибку одной функцией, не разбирая текст сообщения.
Удобно опираться на стандарт RFC 9457 (Problem Details for HTTP APIs):
{
"type": "https://api.example.com/errors/validation",
"title": "Данные не прошли проверку",
"status": 422,
"errors": {
"email": ["Такой email уже зарегистрирован"],
"phone": ["Неверный формат номера"]
}
}
Какие коды использовать
400— запрос нельзя разобрать: сломанный JSON, неверный тип параметра;401— клиент не авторизован,403— авторизован, но прав нет;404— ресурса нет или клиенту нельзя знать, что он существует;409— конфликт состояния: заказ уже оплачен, версия устарела;422— данные понятны, но не прошли бизнес-проверку;429— превышен лимит запросов, с заголовкомRetry-After;5xx— ошибка на стороне сервера; тело без стектрейсов и внутренних деталей.
Фото: Unsplash
Как устроить пагинацию, фильтры и сортировку?
Пагинацию, фильтры и сортировку делают через параметры запроса с одинаковыми именами во всём API. Для небольших списков подходит постраничная пагинация с номером страницы, для лент и больших таблиц — курсорная, которая не пропускает и не дублирует записи при добавлении новых.
GET /orders?status=paid&created_from=2026-10-01&sort=-created_at&cursor=eyJpZCI6MTAyNH0
Ответ со списком всегда содержит данные и метаданные в одинаковой обёртке:
{
"data": [{ "id": 1024, "status": "paid", "total": { "amount": 499000, "currency": "RUB" } }],
"meta": { "next_cursor": "eyJpZCI6MTAwMH0", "has_more": true }
}
Обратите внимание на деньги: сумма — целым числом в минимальных единицах валюты вместе с кодом валюты. Числа с плавающей точкой для денег приводят к ошибкам округления.
Как менять API, не ломая клиентов?
Менять API без поломок можно, если добавлять, а не изменять: новые поля и эндпоинты безопасны, а переименование, удаление и смена типа поля ломают клиентов. Особенно это важно для мобильных приложений — старые версии остаются у пользователей месяцами.
Правила, которые помогают:
- Добавляйте новые поля, не меняя смысл старых; клиенты должны игнорировать незнакомые поля.
- Несовместимые изменения — через новую версию:
/v2/ordersили заголовок версии. - Устаревшие поля помечайте в документации и держите до тех пор, пока ими пользуются.
- Описывайте API в OpenAPI и проверяйте спецификацию в CI: несовместимое изменение должно ронять сборку.
- Логируйте версии клиентов, чтобы знать, когда старое поведение можно отключить.
Контракт API важен и для архитектуры: если фронтенд и мобильные приложения общаются с сервером только через описанный API, бэкенд можно развивать и даже переписывать, не трогая клиентов. Об этом мы писали в статье про монолит и микросервисы.
Коротко
- Ресурсы — существительные, действие задаёт HTTP-метод, коды ответов — по назначению.
- Все ошибки в одном формате, например по RFC 9457, с деталями по полям.
- Пагинация, фильтры и сортировка устроены одинаково во всём API; деньги — целыми числами с валютой.
- Развивайте API добавлением, несовместимые изменения выпускайте новой версией.
- Опишите API в OpenAPI и проверяйте контракт автоматически.
Частые вопросы
Нужна ли версия в адресе API с первого дня?
Префикс /v1 стоит добавить сразу — он ничего не стоит и упрощает выпуск несовместимой версии потом. Но вторую версию лучше выпускать как можно реже, развивая API совместимыми изменениями.
Как документировать REST API?
Спецификацией OpenAPI, которая генерируется из кода или проверяется против него в CI. По ней строится интерактивная документация, клиентские SDK и тесты контракта, поэтому она не расходится с реальностью.
snake_case или camelCase в JSON?
Любой, но один на весь API. Мы обычно выбираем стиль, привычный бэкенду, и не смешиваем: переключение между стилями в разных разделах — частый источник ошибок на клиентах.
Как защитить API от перегрузки?
Лимитами запросов на клиента и эндпоинт с ответом 429 и заголовком Retry-After, кешированием частых чтений и ограничением размера страниц. Тяжёлые операции лучше выполнять асинхронно и возвращать 202.



