← назад к разделу

Слово «REST» все понимают по-разному. Для одного это «JSON по HTTP», для другого — строгий набор правил про ресурсы и методы, а для третьего — ответы, которые сами подсказывают клиенту, что делать дальше. Чтобы не спорить, есть удобная лесенка — модель зрелости Ричардсона: она раскладывает «RESTful-ность» на четыре ступени. Верхняя ступень называется HATEOAS. Разберём, что это, зачем и почему до неё почти никто не доходит — и это нормально.

Модель зрелости Ричардсона

Леонард Ричардсон предложил смотреть на API как на четыре уровня зрелости. Каждый следующий добавляет одну идею.

  • Уровень 0 — один вход. Есть единственный endpoint, например /api, и в него шлют всё подряд, обычно POST с телом вроде «действие: получить заказ, id: 42». HTTP тут — просто труба для передачи данных. Так выглядит классический RPC поверх HTTP.
  • Уровень 1 — ресурсы. Появляются отдельные адреса под сущности: /orders/42, /customers/7. Мы перестаём слать всё в одну точку и начинаем обращаться к конкретным «вещам». Но методы пока используются как попало.
  • Уровень 2 — HTTP-методы и статусы. Мы используем глаголы по назначению: GET читает, POST создаёт, PUT/PATCH меняет, DELETE удаляет. И отвечаем правильными кодами: 200, 201, 404, 409. Именно на этом уровне живёт большинство API, которые называют «REST», — и подробно он разобран в статье про REST API.
  • Уровень 3 — HATEOAS. Ответ содержит не только данные, но и гиперссылки на действия, которые сейчас доступны. Клиент не составляет URL сам — он идёт по ссылкам, как человек кликает по страницам сайта.

Важно: это не оценка «хорошо/плохо». Это описание, сколько идей REST вы применили. Уровень 2 — совершенно рабочая и уважаемая точка.

Что такое HATEOAS

HATEOAS — это аббревиатура от «Hypermedia As The Engine Of Application State»: гипермедиа как движок состояния приложения. Звучит громоздко, а идея простая.

Представьте сайт в браузере. Вы не запоминаете URL каждой страницы — вы видите ссылки и кнопки и кликаете по тем, что доступны сейчас. Если заказ ещё не оплачен, есть кнопка «Оплатить»; после оплаты она пропадает, зато появляется «Вернуть». Браузер не знает заранее структуру сайта — он просто идёт по ссылкам, которые ему прислал сервер.

HATEOAS предлагает то же самое, но для программы-клиента. Сервер вместе с данными присылает список доступных следующих действий в виде ссылок. Клиент не хардкодит адреса вроде /orders/42/cancel — он берёт нужную ссылку из ответа. Если действие сейчас недоступно (заказ уже отменён), ссылки просто не будет, и клиенту не нужно самому знать правила «когда можно отменять».

Пример ответа с гиперссылками

Чаще всего ссылки кладут в отдельный блок. Один из популярных форматов — HAL (Hypertext Application Language), где ссылки лежат под ключом _links:

{
  "id": 42,
  "status": "PAID",
  "amount": 1500,
  "_links": {
    "self":   { "href": "/orders/42" },
    "refund": { "href": "/orders/42/refund" },
    "invoice":{ "href": "/orders/42/invoice" }
  }
}

Что тут происходит. Кроме самих данных заказа сервер сказал: «вот ссылка на меня самого (self), вот действие "вернуть деньги" (refund) и вот "посмотреть счёт" (invoice)». Клиенту не надо знать, как собирается URL возврата, — он берёт готовый href.

А теперь тот же заказ, но уже возвращённый:

{
  "id": 42,
  "status": "REFUNDED",
  "amount": 1500,
  "_links": {
    "self":    { "href": "/orders/42" },
    "invoice": { "href": "/orders/42/invoice" }
  }
}

Ссылки refund больше нет — возвращать нечего. Клиент, который «просто рисует кнопки по ссылкам», автоматически спрячет кнопку возврата. Правило «когда можно вернуть деньги» осталось на сервере, клиенту его дублировать не пришлось.

Зачем это нужно

У HATEOAS есть два честных плюса.

Слабая связанность клиента и сервера. Клиент не зашивает у себя карту URL и правила «когда какое действие доступно». Сервер может поменять адрес возврата с /orders/42/refund на что-то другое — клиент не сломается, потому что берёт ссылку из ответа, а не собирает её сам. Логика «что доступно» живёт в одном месте — на сервере.

Самодокументируемость. Ответ сам показывает, что с ресурсом можно делать дальше. В идеале клиент способен «исследовать» API, переходя по ссылкам от корня, почти как человек ходит по сайту, — не заглядывая постоянно в отдельную документацию.

Почему на практике доходят редко

Теперь честно про минусы — из-за них уровень 3 остаётся скорее теорией.

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

Сложнее клиенту — если он вообще хочет это использовать. Красивая идея «клиент ходит по ссылкам и ничего не хардкодит» требует умного клиента, который умеет находить действия по их именам и реагировать на их появление/исчезновение. На деле почти все клиенты всё равно знают структуру API заранее и просто игнорируют блок _links — тогда вся дополнительная работа сервера пропадает впустую.

Мало инструментов и привычки. Вокруг уровня 2 выстроена вся экосистема: генераторы клиентов, документация OpenAPI, тестовые инструменты. Полноценный HATEOAS-клиент, который динамически ходит по ссылкам, поддерживают единицы, и у команд нет устоявшихся практик.

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

Где это применяется

Отдельные элементы HATEOAS встречаются чаще, чем полный уровень 3. Обычно берут не всё, а самое полезное:

  • Ссылки на связанные ресурсы. Отдать вместе с заказом ссылку на клиента и на счёт — дёшево и удобно, даже если остальное осталось на уровне 2.
  • Пагинация. Классический удачный случай: сервер присылает ссылки next и prev, и клиенту не надо самому клеить query-параметры для следующей страницы.
  • Процессы с состояниями. Там, где у сущности есть жизненный цикл (черновик → оплачен → отправлен → возвращён), ссылки на доступные переходы избавляют клиента от дублирования правил.

Где спотыкаются начинающие

  • Путают «REST» с уровнем 3. Услышав, что «настоящий REST — это HATEOAS», начинают считать свой рабочий API «ненастоящим». Уровень 2 — законная и самая распространённая цель, не комплексуйте.
  • Строят HATEOAS, которым никто не пользуется. Обвешивают ответы ссылками, а клиент всё равно хардкодит URL и игнорирует _links. Получается работа сервера впустую — сначала убедитесь, что клиент реально будет ходить по ссылкам.
  • Изобретают свой формат ссылок в каждом endpoint. Если уж делать, берите готовое соглашение (HAL) единообразно, а не «тут _links, там actions, а рядом просто url».
  • Тащат полный уровень 3 в простой CRUD. Для пары справочников гиперссылки на действия — лишняя сложность без отдачи. Начинайте с чистого уровня 2.

Что учить дальше

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