Как правильно распределять данные в REST API
Когда мы проектируем REST API, один из первых практических вопросов звучит так: какие данные куда класть в запросе?
У нас есть путь, query‑параметры, заголовки и тело — и каждый из этих элементов решает свою задачу.
В этой статье разберёмся на конкретном кейсе: витрина каталога товаров в мобильном приложении.
Кейс: витрина каталога и «глупый» фронт
Представим, что у нас есть фронт: iOS, Android и мобильный веб.
Фронт максимально "глупый": он просто ходит в мидл‑сервисы и хочет получить:
При этом, вместе с запросом фронт передаёт:
Задача: понять, в какую часть HTTP‑запроса какие данные класть, чтобы запросы были логичными, безопасными и удобными для поддержки.
Четыре места для данных в HTTP‑запросе
В типичном REST API у нас есть четыре основных "слота", куда можно положить данные:
- Путь (Path) — часть URL, которая описывает, к какому ресурсу мы обращаемся.
- Параметры запроса (Query) — строка после
?в URL, используется для фильтров, сортировок и прочих модификаторов выборки. - Заголовки (Headers) — метаданные запроса: авторизация, формат данных, информация о клиенте.
- Тело (Body) — основное содержимое: то, что мы создаём, изменяем или отправляем как сложный запрос.
Дальше разложим наш кейс по этим четырём местам.
Авторизационный токен: заголовок Authorization
Bearer‑токен (или любой другой токен авторизации) логичнее всего передавать в заголовке Authorization.
Это де‑факто стандарт для REST API и рекомендованный способ передачи авторизационных данных.
GET /api/v1/products Authorization: Bearer eyJhbGciOiJIUz...
- токен не попадает в URL, а значит — не засветится в истории браузера или прокси
- многие инструменты и библиотеки по умолчанию ожидают токен именно в
Authorization - это согласуется с практиками OAuth 2.0 и большинством публичных API
Метаданные клиента: тип приложения, версия, ОС
Информация о том кто обращается, мобилка, веб. Это метаданные: они описывают не ресурс, а того, кто делает запрос.
Примеры:
Эти данные нужны серверу, чтобы:
- принимать решения по фичефлагам (например, отключать функциональность для старых версий)
- логировать и анализировать трафик по типам клиентов
- внедрять ограничения: «для версии ниже 1.5 не отдаём новый тип карточек товара»
Так как эти данные нужны при любом запросе, а не только при конкретных методах, логично передавать их в заголовках.
X-App-Type: ios X-App-Version: 1.2.3 X-OS-Name: iOS X-OS-Version: 16.4
Исторически было принято использовать префикс X- для кастомных заголовков, но сейчас его применение считается устаревшей практикой.
Можно использовать более нейтральные варианты, например: App-Type, App-Version, Client-OS-Name.
Ключевой момент: не класть эти метаданные в тело, потому что:
- у GET‑запросов тела обычно нет, а метаданные нам нужны и там
- нам важна предсказуемость: любой запрос несёт одни и те же заголовки клиента
- прокси и кэширующие слои чаще учитывают заголовки, чем тело, для принятия решений
Фильтры и сортировка: параметры запроса (query)
Когда речь идёт о фильтрации, поиске или сортировке, первый кандидат — query‑параметры.
Они позволяют явно описать, какую выборку мы хотим получить.
GET /api/v1/products?sort=price&category=electronics&page=1&limit=20
sort=price— сортировка по ценеcategory=electronics— фильтрация по категорииpageиlimit— параметры пагинации
Рекомендации по фильтрам через query:
- использовать понятные названия (
price,category,brand,sort) - делать параметры консистентными: например,
page/limitвезде, а неpage/perPageв одном методе иoffset/countв другом. - для сложных фильтров применять единый формат (например,
filter[field]=valueилиfilter=field:op:value)
Когда фильтры уходят в тело запроса
Бывает, что фильтрация становится настолько сложной, что query‑строка становится:
В таких случаях разумно перенести фильтры в тело запроса и использовать POST для получения данных по сложному запросу.
POST /api/v1/products/search
Content-Type: application/json
{
"sort": { "field": "price", "direction": "asc" },
"filters": {
"category": ["electronics", "appliances"],
"price": { "gte": 1000, "lte": 5000 },
"inStock": true
},
"page": 1,
"limit": 20
}Такой подход встречается, когда нужны:
Правило, которое удобно держать в голове:
простые фильтры — в query, сложные и структурированные — в теле запроса.
И финальное, все данные, которые мы получаем о товарах мы забираем из тела запроса.