Logo
Overview

REST API: основные принципы — полный разбор

August 19, 2026
12 min read

REST — это не про JSON. Не про /api/v1. И уж точно не про «у нас POST на всё, и так нормально работает». За аббревиатурой REST стоит архитектурный стиль, который Рой Филдинг описал в своей диссертации в 2000 году. Он сформулировал шесть ограничений. Шесть. Не «желательно», не «если удобно» — а именно ограничений, которые вместе и образуют REST.

Проблема в том, что большинство «REST API», которые мы видим в продакшене, соблюдают от силы два-три принципа. И ничего — работают. Вопрос не в том, можно ли нарушать, а в том, какую цену вы за это платите. Каждый проигнорированный принцип аукается позже: отсутствием кеширования, невозможностью вставить балансировщик, хрупкостью клиентов или отладкой, которая превращается в гадание.

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

Что такое REST: от диссертации Филдинга до продакшена

REST (Representational State Transfer) — архитектурный стиль для распределённых систем, описанный Роем Филдингом в 2000 году. Определяет шесть ограничений, которым должна удовлетворять система, чтобы считаться RESTful.

Филдинг — один из авторов протокола HTTP. Он не придумывал REST на пустом месте: он описал архитектурные принципы, на которых уже был построен Web. Его диссертация — это не «как сделать API», а «почему Web масштабировался, а другие распределённые системы того времени — нет».

Шесть ограничений REST (их часто называют принципами):

  1. Клиент-сервер (Client-Server) — разделение ответственности между UI и хранилищем данных.
  2. Отсутствие состояния (Stateless) — каждый запрос содержит всю информацию, сервер не хранит сессию клиента между запросами.
  3. Кешируемость (Cacheability) — ответы явно помечаются как кешируемые или некешируемые.
  4. Многослойность (Layered System) — клиент не знает, общается он с конечным сервером или с промежуточным узлом.
  5. Единообразие интерфейса (Uniform Interface) — единые правила взаимодействия: ресурсы, методы, представления, HATEOAS.
  6. Код по требованию (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

Соберём все шесть принципов на одной схеме. Путь запроса от клиента до базы данных и обратно — и на каких этапах какие принципы работают:

100%
graph TB
  subgraph ClientLayer["Слой клиента"]
      C1["Веб-приложение SPA"]
      C2["Мобильное приложение"]
      C3["Сторонний сервис"]
  end
  
  subgraph IntermediaryLayer["Промежуточные слои"]
      CDN["CDN / Кеш"]
      GW["API Gateway"]
  end
  
  subgraph ServerLayer["Серверный слой"]
      API["REST API сервер"]
      SVC["Бизнес-логика"]
      DB["База данных"]
  end
  
  C1 --> CDN
  C2 --> CDN
  C3 --> CDN
  CDN --> GW
  GW --> API
  API --> SVC
  SVC --> DB
  
  style C1 fill:#4a90d9,stroke:#2c5f8a,color:#fff
  style C2 fill:#4a90d9,stroke:#2c5f8a,color:#fff
  style C3 fill:#4a90d9,stroke:#2c5f8a,color:#fff
  style CDN fill:#f0a500,stroke:#c88400,color:#fff
  style GW fill:#e0e0e0,stroke:#999,color:#333
  style API fill:#50c878,stroke:#3a9a5c,color:#fff
  style SVC fill:#50c878,stroke:#3a9a5c,color:#fff
  style DB fill:#7b68ee,stroke:#5a4db2,color:#fff
  style ClientLayer fill:#e8f0fe,stroke:#4a90d9,color:#2c5f8a
  style IntermediaryLayer fill:#fff8e1,stroke:#f0a500,color:#c88400
  style ServerLayer fill:#e8f5e9,stroke:#50c878,color:#3a9a5c

На схеме три архитектурных слоя:

  • Слой клиента (синий) — три разных клиента: 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 или PayPal95% внутренних 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.