Stage 2. REST Essentials
REST — это практичный подход к проектированию HTTP API, который помогает сохранить понятную структуру по мере роста проекта. Базовая идея простая: API строится вокруг ресурсов, а поведение операций задаётся стандартной семантикой HTTP.
Ресурсом считается объект предметной области: books, users, orders, tasks. В REST путь URL идентифицирует ресурс, а HTTP-метод определяет, что нужно сделать с этим ресурсом.
Базовая модель REST
Надёжная последовательность проектирования:
- Определить имена ресурсов как существительные.
- Определить пути для коллекции и одного элемента.
- Сопоставить операции с HTTP-методами.
- Зафиксировать статус-коды и формат ответов.
Базовая карта ресурсов:
GET /books
POST /books
GET /books/{id}
PUT /books/{id}
DELETE /books/{id}
Этот шаблон легко повторять в разных модулях, поэтому контракт API остаётся предсказуемым.
Принципы, которые реально важны
Ресурсная модель означает, что путь показывает объект, с которым вы работаете, а не команду. Маршрут /books ресурсный, а /createBook командный.
Stateless-коммуникация означает, что каждый запрос содержит достаточно контекста для независимой обработки. Сервер не должен зависеть от скрытого состояния UI между вызовами.
Единообразный интерфейс означает, что похожие ситуации дают похожее поведение. Если два endpoint возвращают ошибку валидации, у них должен быть сопоставимый класс status code и единая форма error payload.
Семантика методов должна оставаться чистой: GET читает, POST создаёт, PUT заменяет, DELETE удаляет.
Именование и структура URL
Используйте существительные в путях и избегайте глаголов в названиях маршрутов. Не дублируйте действие в URL, если оно уже задано методом.
Избегайте:
/createBook/updateBook/12/deleteBook/12
Предпочитайте:
POST /booksPUT /books/12DELETE /books/12
Путь коллекции обычно во множественном числе (/books), путь одного ресурса содержит идентификатор (/books/{id}), а вложенность должна быть умеренной и обоснованной.
Path-параметры и query-параметры
Path-параметр используйте тогда, когда значение однозначно указывает на один ресурс. Query-параметры используйте для фильтрации, сортировки, поиска и пагинации коллекции.
GET /books/12— книга с id 12.GET /books?author=rowling— список книг с фильтром по автору.GET /books?sort=title&page=2&size=20— список с сортировкой и пагинацией.
Если параметр обязателен для поиска конкретного объекта, это path. Если параметр меняет представление списка, это query.
Дисциплина status codes
Status codes — это не декоративная часть ответа, а протокольный сигнал для клиента, логов и мониторинга.
| Ситуация | Рекомендуемый status | Почему |
|---|---|---|
| Список успешно получен | 200 OK | Успешное чтение данных |
| Ресурс создан | 201 Created | На сервере создан новый объект |
| Ресурс обновлён с возвратом тела | 200 OK | Обновление успешно, данные возвращены |
| Ресурс удалён | 204 No Content | Удаление успешно, тело не требуется |
| Некорректный ввод | 400 Bad Request | Ошибка формата или валидации запроса |
| Ресурс не найден | 404 Not Found | Целевой объект отсутствует |
| Конфликт состояния | 409 Conflict | Запрос конфликтует с текущим состоянием ресурса |
Если возвращать 200 во всех случаях, клиент теряет протокольный смысл и усложняется интеграция.
Единый формат ответов и ошибок
Для коллекций формат-обёртка обычно лучше, чем “голый массив”. Так проще добавлять метаданные без ломающих изменений.
Пример ответа списка:
{
"items": [
{ "id": 12, "title": "Clean Code" },
{ "id": 13, "title": "Refactoring" }
],
"meta": {
"page": 1,
"size": 20,
"total": 125
}
}
Пример ответа с ошибкой:
{
"error": "validation_failed",
"message": "title is required",
"details": [
{ "field": "title", "reason": "must not be blank" }
],
"traceId": "a4f8d2e1"
}
Стабильная структура ошибок упрощает обработку на frontend и ускоряет диагностику инцидентов.
Практический сценарий: онлайн-библиотека
Предположим, API управляет книгами.
Пользователь открывает каталог. Клиент отправляет GET /books?page=1&size=20. Сервер возвращает список с полями items и meta.
Пользователь добавляет книгу. Клиент отправляет POST /books с названием и автором. Сервер создаёт запись и возвращает 201 Created вместе с созданным объектом.
Пользователь редактирует книгу 12. Клиент отправляет PUT /books/12 с обновлённым состоянием. Сервер валидирует поля, обновляет запись и возвращает 200 OK.
Пользователь пытается открыть удалённую книгу 99. Клиент отправляет GET /books/99. Сервер возвращает 404 Not Found в стандартном error формате.
Пользователь удаляет книгу 12. Клиент отправляет DELETE /books/12. Сервер удаляет запись и возвращает 204 No Content.
Этот сценарий покрывает базу REST: ресурсные пути, методы, status codes, метаданные списка и единый формат ошибок.
Базовый подход к versioning
Версию API лучше фиксировать явно с самого начала. Частый вариант: префикс в пути, например /api/v1/books.
Польза в том, что при будущих несовместимых изменениях можно добавить /api/v2, не ломая сразу всех существующих клиентов.
Чеклист REST best practices
- Используйте ресурсные пути на существительных.
- Держите единый стиль именования (lowercase, множественное число для коллекций).
- Не используйте глаголы в URL.
- Применяйте методы по семантике HTTP.
- Возвращайте осмысленные status codes под сценарий.
- Держите единый формат успешных и error-ответов.
- Используйте query-параметры для фильтрации, сортировки и пагинации.
- Контролируйте глубину вложенности URL.
- Добавьте версионирование API до публичного масштабирования.
Частые ошибки
Распространённая ошибка — строить API как RPC и создавать команды вида /run, /execute, /process для большинства операций. Такая схема скрывает ресурсную модель и усложняет интеграцию.
Другая ошибка — непоследовательная обработка ошибок, когда один endpoint возвращает структурированную валидацию, а другой обычный текст. В итоге клиентский код дробится на множество частных случаев.
Ещё одна ошибка — избыточно глубокие пути для каждой связи между сущностями. Это ухудшает читаемость и усиливает связь клиента с внутренней структурой backend.
REST не требует сложного фреймворка, чтобы начать правильно. Нужны последовательные решения на уровне API-контракта. Если эти решения стабильны, систему проще развивать и поддерживать на длинной дистанции.