CRUD-операций (создать, прочитать, обновить, удалить) хватает не всегда. Иногда нужно сослаться на ресурс без конкретного ID — «мой профиль», «последний деплой». Иногда нужно выразить бизнес-команду — «подтвердить заказ», «отменить подписку». Для обоих случаев в REST есть устойчивые приёмы.
Alias-сегменты: shortcut вместо ID
Обычный REST-путь выглядит так: GET /users/42. Клиент знает ID пользователя и подставляет его. Но что если клиент не знает ID — он хочет «текущего пользователя», «последний деплой», «основную платёжную карту»?
Alias-сегмент — это зарезервированное слово в пути вместо ID. Сервер сам понимает из контекста, какой конкретно объект имеется в виду.
me — псевдоним для текущего пользователя
GET /users/42 ← admin смотрит на любого пользователя
GET /users/me ← обычный пользователь смотрит на себя
me — сокращение, которое сервер разворачивает в ID из токена авторизации. Клиенту не нужно хранить свой userId отдельно.
Когда me нужен. Задайте себе вопрос: «Мог бы администратор использовать этот же эндпоинт с чужим ID?» Если да — me полезен. Обычный пользователь ставит me, администратор ставит конкретный ID.
GET /users/me ✓ — администратор мог бы GET /users/42
GET /users/me/settings ✓ — настройки профиля
Когда me не нужен. Если эндпоинт всегда работает только с данными вызывающего и нет никакого «посмотреть чужое», me добавляет шум без пользы:
GET /users/me/orders ✗ — заказы и так берутся из токена, /orders достаточно
GET /orders ✓ — текущий пользователь видит только свои заказы
Ещё одно ограничение: me — это псевдоним для конкретного пользователя в коллекции users. Путь /me без users/ в начале не имеет смысла и использоваться не должен.
GET /me ✗ — непонятно, к какому ресурсу относится
GET /users/me ✓
me используют GitHub API, Google API, Spotify API, Microsoft Graph — это устоявшийся подход. Альтернативы (self, current-user) встречаются реже.
Временные и порядковые alias
Для выборки «крайнего» объекта из коллекции вместо длинных параметров можно использовать слова-псевдонимы:
GET /deployments/latest — последний деплой (вместо ?sort=createdAt&order=desc&limit=1)
GET /subscriptions/current — текущая подписка
GET /invoices/next — следующий счёт
GET /billing-periods/previous — предыдущий расчётный период
Это работает только для singleton-выборки — когда контекст однозначно определяет один объект. Если таких объектов может быть несколько, нужна обычная фильтрация с параметрами.
Логические alias
Похожий приём для объектов, выделенных по бизнес-признаку:
GET /payment-methods/default — платёжный метод «по умолчанию»
GET /addresses/primary — основной адрес
GET /plans/active — активный тарифный план
GET /documents/draft — черновик
Каждый из них — ровно один объект, не список с фильтром.
Action-эндпоинты: доменные команды
Есть операции, которые не вписываются в CRUD. «Подтвердить заказ» — это не создание и не обновление в обычном смысле. Это бизнес-команда, у которой есть имя, побочные эффекты и, возможно, входные данные.
Для таких случаев используют action-эндпоинт: ресурс плюс глагол действия.
POST /orders/{id}/confirm
POST /orders/{id}/cancel
POST /orders/{id}/ship
POST /orders/{id}/refund
Как выглядит action-эндпоинт
Путь: ресурс с ID, затем глагол в инфинитиве.
Глагол — именно инфинитив, не существительное и не причастие:
POST /orders/{id}/confirm ✓
POST /orders/{id}/confirmation ✗ — существительное
POST /orders/{id}/confirmed ✗ — причастие
Метод — всегда POST. Даже если операция технически идемпотентна (повторный вызов даёт тот же результат), используется POST. Причина простая: action — это команда, а не замена ресурса. PUT означает «положи вот это состояние», POST означает «выполни вот это действие». Семантика важнее.
Входные данные — в теле запроса:
POST /orders/{id}/ship
Content-Type: application/json
{
"trackingNumber": "TR-123456",
"carrier": "DHL"
}
Если параметров нет, тело может быть пустым.
Когда action, а когда PATCH
Частая дилемма: изменить статус заказа — это PATCH /orders/{id} с { "status": "CONFIRMED" } или POST /orders/{id}/confirm?
Ориентир простой: если у операции есть доменное имя и побочные эффекты (события, переходы состояний, уведомления) — это action. Если просто меняется значение поля без особой логики — PATCH.
PATCH /orders/{id} { "description": "..." } ✓ — простое обновление поля
POST /orders/{id}/confirm ✓ — доменная команда, статус-машина
POST /orders/{id}/cancel ✓ — есть имя, есть побочные эффекты
PATCH /orders/{id} { "status": "CANCELLED" } ✗ — скрывает доменный смысл за полем
Action-эндпоинты делают API читаемым: в логах сразу видно POST /orders/42/confirm, а не абстрактное «order updated». Разграничение прав тоже проще — для каждого действия своё разрешение.
Частые ошибки
me там, где эндпоинт работает только с данными текущего пользователя. Если нет admin-сценария с чужим ID, me не добавляет смысла — он просто лишний сегмент.
/me без users/. me — псевдоним для конкретного пользователя внутри коллекции users, не самостоятельный путь.
Существительное или причастие в action. /orders/{id}/confirmation — это выглядит как ресурс, а не команда. Правильно: /orders/{id}/confirm.
PUT для action. PUT /orders/{id}/confirm — семантически противоречие. PUT заменяет ресурс, POST выполняет команду.
Длинное имя action. cancelTheOrderImmediately — лишние слова. Достаточно cancel.
Коротко
- Alias-сегмент — зарезервированное слово вместо ID; сервер разворачивает его из контекста.
me— псевдоним для текущего пользователя; нужен только когда есть admin-сценарий с чужим ID. Путь/meбезusers/не используется.- Временные alias (
latest,current,next,previous) — для singleton-выборки крайнего объекта. - Логические alias (
default,primary,active,draft) — для объекта, выделенного по бизнес-признаку. - Action-эндпоинт — для доменных команд, у которых есть имя и побочные эффекты: ресурс + глагол-инфинитив, метод
POSTвсегда. - Выбор между action и
PATCH: есть доменное имя и побочные эффекты → action; простое изменение поля →PATCH.
Что почитать дальше
- URL и ресурсы в REST API — структура пути, именование ресурсов.
- HTTP-методы — семантика GET, POST, PUT, PATCH, DELETE.
- Версионирование REST API — как эволюционировать эндпоинты без поломки клиентов.