Слово «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. А как выбор стиля контракта вписывается в проектирование системы целиком — в разделе системного дизайна.