REST — это не про JSON. Не про /api/v1. И уж точно не про «у нас POST на всё, и так нормально работает». За аббревиатурой REST стоит архитектурный стиль, который Рой Филдинг описал в своей диссертации в 2000 году. Он сформулировал шесть ограничений. Шесть. Не «желательно», не «если удобно» — а именно ограничений, которые вместе и образуют REST.
Проблема в том, что большинство «REST API», которые мы видим в продакшене, соблюдают от силы два-три принципа. И ничего — работают. Вопрос не в том, можно ли нарушать, а в том, какую цену вы за это платите. Каждый проигнорированный принцип аукается позже: отсутствием кеширования, невозможностью вставить балансировщик, хрупкостью клиентов или отладкой, которая превращается в гадание.
Давайте пройдёмся по всем шести принципам. Не как по списку из учебника — а с примерами, где каждый принцип реально спасает (или его нарушение реально топит).
Что такое REST: от диссертации Филдинга до продакшена
REST (Representational State Transfer) — архитектурный стиль для распределённых систем, описанный Роем Филдингом в 2000 году. Определяет шесть ограничений, которым должна удовлетворять система, чтобы считаться RESTful.
Филдинг — один из авторов протокола HTTP. Он не придумывал REST на пустом месте: он описал архитектурные принципы, на которых уже был построен Web. Его диссертация — это не «как сделать API», а «почему Web масштабировался, а другие распределённые системы того времени — нет».
Шесть ограничений REST (их часто называют принципами):
- Клиент-сервер (Client-Server) — разделение ответственности между UI и хранилищем данных.
- Отсутствие состояния (Stateless) — каждый запрос содержит всю информацию, сервер не хранит сессию клиента между запросами.
- Кешируемость (Cacheability) — ответы явно помечаются как кешируемые или некешируемые.
- Многослойность (Layered System) — клиент не знает, общается он с конечным сервером или с промежуточным узлом.
- Единообразие интерфейса (Uniform Interface) — единые правила взаимодействия: ресурсы, методы, представления, HATEOAS.
- Код по требованию (Code on Demand) — сервер может передать клиенту исполняемый код (например, JavaScript).
Первые пять — обязательные. Шестой — опциональный, и на практике его почти никто не использует за пределами браузерного Web. Но для полноты картины стоит разобрать и его.
Шесть принципов REST API — детальный разбор
1. Клиент-сервер (Client-Server)
Самый простой и самый недооценённый принцип. Звучит тривиально: клиент занимается интерфейсом, сервер — данными и бизнес-логикой. Но на практике граница между ними размывается постоянно.
Что даёт разделение:
- Клиент и сервер могут развиваться независимо. Вы переписываете фронтенд с React на Vue — сервер об этом не знает. Вы меняете базу данных с PostgreSQL на MongoDB — клиенту всё равно, пока API-контракт не изменился.
- Сервер не зависит от того, кто к нему стучится: браузер, мобильное приложение или другой сервис. Один и тот же
GET /api/orders/42работает для всех.
Где нарушают: Когда сервер начинает генерировать HTML с вкраплениями данных, смешивая логику отображения и бизнес-логику. RESTful-сервер возвращает данные (JSON, XML), а не готовую страницу. Серверный рендеринг — отдельный архитектурный паттерн, и он не имеет отношения к REST API.
Где нарушают иначе: Когда клиент начинает додумывать бизнес-правила. «Если у заказа статус draft, покажем кнопку удаления» — ок, это UI-логика. «Если сумма заказа больше 100 000, отправим два запроса по 50 000» — стоп. Это бизнес-правило должно жить на сервере. Клиент не умнеет от того, что вы перенесли логику к нему, — он становится хрупким.
2. Отсутствие состояния (Stateless)
Принцип, вокруг которого сломано больше всего копий. Формулировка простая: каждый запрос от клиента к серверу должен содержать всю информацию, необходимую для его обработки. Сервер не хранит состояние клиента между запросами.
Что это значит на практике:
- Нет серверных сессий. Сервер не помнит, что клиент «авторизован». Токен авторизации передаётся в каждом запросе — в заголовке
Authorization. - Нет «корзины на сервере». Состояние корзины либо хранится в базе, либо живёт на клиенте, а сервер получает полный список товаров в запросе на оформление заказа.
- Балансировщик может отправить первый запрос на сервер A, а второй — на сервер B. Оба обработают запрос одинаково, потому что никакого контекста не требуется.
Что даёт stateless:
- Горизонтальное масштабирование из коробки. Добавили сервер за балансировщиком — он сразу работает.
- Восстановление после сбоя. Сервер упал — новый экземпляр подхватывает запросы без потери «сессий».
- Простота мониторинга. Каждый запрос изолирован, не нужно восстанавливать контекст.
Где нарушают: Классика — серверная сессия с идентификатором в куке. Клиент логинится, сервер создаёт сессию в памяти и возвращает Set-Cookie: session_id=abc123. Следующие запросы клиент отправляет с кукой, сервер ищет сессию в своём локальном хранилище. Работает. Ровно до того момента, пока у вас не появится второй сервер. Балансировщик отправляет запрос на сервер B, у которого сессии abc123 нет — клиент «разлогинился». Лечится sticky sessions, общим хранилищем сессий (Redis) — но это уже обход ограничения, а не REST.
Альтернатива: JWT-токен. Клиент получает токен при логине, подписывает им каждый запрос. Сервер проверяет подпись, извлекает user_id из токена — и всё. Сессий на сервере нет. Масштабируется без танцев с Redis.
Если хотите глубже разобраться в теме stateless и понять, где граница между stateless и stateful в реальных системах, — загляните в разбор stateless и stateful API.
3. Кешируемость (Cacheability)
Принцип, который экономит деньги и нервы, но про него часто забывают до первого дашборда с нагрузкой. Формулировка: ответы сервера должны явно помечаться как кешируемые или некешируемые.
Как это работает:
- Сервер добавляет заголовок
Cache-Control: max-age=3600— ответ можно кешировать на час. - Сервер добавляет
Cache-Control: no-store— ответ нельзя кешировать ни при каких условиях. - Промежуточные узлы (браузер, CDN, прокси) читают заголовки и решают: сохранить ответ в кеш или каждый раз ходить на сервер.
Что даёт кешируемость:
- Снижение нагрузки на сервер. Запрос, который вернулся из кеша CDN, вообще не дошёл до вашего бэкенда.
- Снижение задержки. Клиент получает ответ от ближайшего узла CDN, а не от вашего сервера в другом регионе.
- Устойчивость к всплескам трафика. Кеш сглаживает пики.
Где нарушают: Либо не выставляют заголовки кеширования вообще (и тогда прокси угадывают, можно кешировать или нет), либо кешируют то, что нельзя. Классика: GET /api/orders?status=active возвращает Cache-Control: max-age=3600. Час из кеша отдаются одни и те же заказы, хотя за это время три заказа перешли в статус completed, а два новых появилось. Клиент видит устаревшие данные.
Правило: кешируем то, что меняется редко или предсказуемо (справочники, каталог товаров, контентные страницы). Не кешируем то, что должно быть актуальным здесь и сейчас (статус заказа, баланс счёта, количество товара на складе). И обязательно выставляем Cache-Control явно — не полагаемся на «умолчания» прокси.
4. Многослойность (Layered System)
Принцип, который звучит абстрактно, а на деле — это то, что позволяет незаметно воткнуть балансировщик, кеш или API Gateway между клиентом и сервером. Формулировка: клиент не знает, общается он с конечным сервером или с промежуточным узлом.
Как это выглядит в реальности:
- Клиент шлёт запрос на
https://api.example.com. Он понятия не имеет, что сначала запрос попадает на Cloudflare (CDN), потом на Nginx (балансировщик), потом на Kong (API Gateway), и только потом — на ваш бэкенд. - Каждый промежуточный слой может модифицировать запрос или ответ, добавить аутентификацию, собрать метрики, обрезать тело ответа.
- Добавление нового слоя (например, WAF для защиты от атак) не требует изменений ни в клиенте, ни в сервере.
Что даёт многослойность:
- Архитектурную гибкость. Вы можете пересобрать инфраструктуру, не трогая код клиента и сервера.
- Разделение ответственности. Балансировщик занимается распределением нагрузки, API Gateway — rate limiting’ом, бэкенд — бизнес-логикой.
- Безопасность. Промежуточный слой может терминировать TLS, проверять JWT, отсекать подозрительные запросы — бэкенд получает уже чистый трафик.
Где нарушают: Когда клиент жёстко завязывается на конкретный сервер. «Я стучусь на backend-1.internal:8080» — это не REST, это прямое соединение, и многослойность тут не работает. RESTful-клиент знает только URL API, всё остальное — забота инфраструктуры.
Важный нюанс: многослойность не означает бесконечную вложенность. Каждый слой добавляет задержку. Три слоя (CDN → Gateway → Backend) — норма. Десять слоёв — архитектурный запах, который стоит почистить.
5. Единообразие интерфейса (Uniform Interface)
Самый объёмный принцип. Филдинг разбил его на четыре подпринципа, и каждый заслуживает отдельного разговора:
5a. Идентификация ресурсов (Resource identification). Каждый ресурс имеет уникальный идентификатор — URL. Не /getOrder?id=42, а /orders/42. Ресурс — это не функция, это сущность. URL указывает на сущность, HTTP-метод говорит, что с ней сделать.
5b. Манипуляция ресурсами через представления (Resource manipulation through representations). Клиент получает представление ресурса (JSON), модифицирует его и отправляет обратно. Серверу не нужно знать, какой клиент прислал запрос — представление самодостаточно.
5c. Самоописываемые сообщения (Self-descriptive messages). Каждый запрос и ответ содержит достаточно информации для обработки: заголовок Content-Type говорит, в каком формате тело, статус-код — чем закончилась операция, заголовки кеширования — можно ли сохранить ответ. Серверу не нужно догадываться.
5d. HATEOAS (Hypermedia as the Engine of Application State). Самый редко реализуемый подпринцип. Сервер возвращает не только данные, но и ссылки на доступные действия. Например, ответ на GET /orders/42 может содержать:
{ "id": 42, "status": "draft", "total": 15000, "_links": { "confirm": { "href": "/orders/42/confirm", "method": "POST" }, "cancel": { "href": "/orders/42/cancel", "method": "POST" }, "self": { "href": "/orders/42", "method": "GET" } }}Клиент не хардкодит URL для подтверждения заказа — он читает _links.confirm.href из ответа. Поменялся URL? Клиенту всё равно, он идёт по ссылке. Сервер полностью контролирует навигацию.
HATEOAS — мощная идея, которую почти никто не реализует в полном объёме. Для внутренних API это часто избыточно: команда и так знает все URL. Но для публичных API или микросервисов с частыми изменениями — это снижает coupling между сервисами.
Если вы проектируете свой REST API и хотите избежать типичных ошибок в названиях ресурсов — посмотрите правила названий эндпоинтов. Это прямое продолжение подпринципа «идентификация ресурсов»: как назвать, чтобы было понятно и клиенту, и через полгода вам самим.
6. Код по требованию (Code on Demand)
Единственный опциональный принцип. Сервер может отправить клиенту исполняемый код — JavaScript-виджет, скрипт валидации, логику форматирования данных.
Где это работает: Браузерный Web. Сервер возвращает HTML, внутри — тег `<script>` с JavaScript, и браузер исполняет его на стороне клиента. Это буквально «код по требованию».
Где это не работает: В REST API между серверами или в mobile API. Мобильное приложение не будет исполнять присланный сервером JavaScript — это дыра в безопасности размером с грузовик. Поэтому в контексте API этот принцип обычно опускают.
Тем не менее, есть аналоги: сервер может вернуть конфигурацию (правила валидации полей, список допустимых значений enum, схему формы). Это не «исполняемый код» в смысле Филдинга, но дух принципа сохраняется: сервер управляет поведением клиента, не требуя обновления клиентского приложения.
Как принципы выглядят в реальном API
Соберём все шесть принципов на одной схеме. Путь запроса от клиента до базы данных и обратно — и на каких этапах какие принципы работают:
На схеме три архитектурных слоя:
- Слой клиента (синий) — три разных клиента: SPA, мобильное приложение и сторонний сервис. Все они общаются с одним API. Это принцип клиент-сервер в действии: сервер не различает, кто к нему пришёл, контракт един для всех.
- Промежуточные слои (жёлтый и серый) — CDN и API Gateway. Клиент не знает об их существовании — он просто шлёт запрос на
api.example.com. Это принципы многослойности (клиент не в курсе, сколько слоёв за URL) и кешируемости (CDN сохраняет ответы согласно заголовкамCache-Control). - Серверный слой (зелёный и фиолетовый) — REST API сервер, бизнес-логика и база данных. Каждый запрос приходит со всей необходимой информацией (токен, параметры) — это stateless. Ответ содержит статус-код,
Content-Typeи заголовки кеширования — это единообразие интерфейса и самоописываемые сообщения.
Обратите внимание: на схеме нет серверной сессии. Нет sticky sessions между Gateway и API. Нет хранилища состояния клиента на сервере. Всё, что нужно для обработки запроса, приходит в самом запросе — либо извлекается из базы по переданному идентификатору.
Что соблюдают все, а что — почти никто
Если пройтись по реальным API и честно оценить их по шести принципам, картина получается такая:
| Принцип | Кто соблюдает | Кто игнорирует |
|---|---|---|
| Клиент-сервер | Почти все. Разделение фронтенда и бэкенда — давно стандарт | Монолитные приложения с серверным рендерингом, которые называют «REST API» |
| Stateless | Большинство, но с оговорками. JWT-токены вытеснили серверные сессии | Легаси-системы с сессиями; команды, которые «пока оставим Redis для сессий» |
| Кешируемость | Команды, которые столкнулись с нагрузкой и смотрят на счета за сервер | Стартапы на ранней стадии: «у нас 100 пользователей, кеш подождёт» |
| Многослойность | Все, кто использует облачных провайдеров (Cloudflare, AWS API Gateway) | Self-hosted решения без reverse proxy перед бэкендом |
| Единообразие интерфейса | Команды, которые договорились о конвенциях | Команды без code review для API-контрактов |
| HATEOAS (подпринцип 5) | Практически никто за пределами публичных API вроде GitHub или PayPal | 95% внутренних API и микросервисов |
| Код по требованию | Браузерный Web | Все остальные |
Примечательно, что HATEOAS и код по требованию — два принципа, которые сам Филдинг считал ключевыми для «настоящего REST». И ровно их на практике игнорируют чаще всего. Это не значит, что «REST умер». Это значит, что индустрия выбрала прагматичный REST — с ресурсами, методами, статус-кодами и stateless, но без гипермедиа-навигации и исполняемого кода на клиенте.
И это нормально. REST — архитектурный стиль, не спецификация. Нет сертификационного органа, который проверит ваш API и скажет «не REST». Есть только компромисс между чистотой архитектуры и практической целесообразностью.
Заключение
Шесть принципов REST — не чек-лист «сделал и забыл». Это набор архитектурных решений, каждое из которых имеет цену и выгоду. Клиент-сервер и stateless — база, без которой REST API превращается в монолит с HTTP-обёрткой. Кешируемость и многослойность — то, что отделяет API, который выдерживает нагрузку, от API, который падает при первом всплеске трафика. Единообразие интерфейса — дисциплина, которая экономит часы отладки и онбординга новых разработчиков.
А HATEOAS… Пусть останется красивой идеей в диссертации Филдинга. Когда ваш API дорастёт до масштабов GitHub API, вы к нему вернётесь.
PS. Если ваш «REST API» использует только POST, хранит сессии в памяти и возвращает 200 OK на любой запрос с телом {"error": "что-то пошло не так"} — вы написали RPC. И это не плохо, просто не называйте это REST.