Содержание

Проектирование REST API

REST API - это договор между клиентом и сервером. Клиентом может быть frontend, мобильное приложение, другой backend-сервис или CLI. Сервер обещает: если вы отправите такой запрос, с такими данными и таким методом, вы получите такой ответ.

Хороший API предсказуем. Разработчик не должен угадывать, почему книги лежат в /books, пользователи в /getUsers, а удаление делается через /remove-book. Чем меньше сюрпризов, тем проще писать frontend, тесты и интеграции.

Ресурсы, а не действия

URL должен описывать сущность, а не команду:

GET /books GET /books/{id} POST /books PATCH /books/{id} DELETE /books/{id}

Плохо:

/getBooks /createBook /deleteBook

В REST действие уже есть в HTTP-методе. URL отвечает на вопрос "над чем работаем", метод отвечает на вопрос "что делаем".

Аналогия: /books/42 - это карточка книги в библиотеке. GET означает "покажи карточку", PATCH - "измени часть карточки", DELETE - "убери карточку".

HTTP-методы

Частые методы:

МетодСмысл
GETполучить данные
POSTсоздать ресурс или запустить операцию
PUTполностью заменить ресурс
PATCHчастично обновить ресурс
DELETEудалить ресурс

GET не должен менять данные. Это важно для кэшей, браузеров, прокси и повторных запросов.

POST обычно используют для создания:

POST /books

PATCH удобен, когда меняется часть ресурса:

{ "title": "New title" }

Статус-коды

Статус-код - короткий итог запроса.

КодКогда использовать
200 OKуспешное чтение или обновление
201 Createdресурс создан
202 Acceptedзадача принята, но еще выполняется
204 No Contentуспешно, тела ответа нет
400 Bad Requestнекорректный ввод
401 Unauthorizedпользователь не аутентифицирован
403 Forbiddenдоступа нет
404 Not Foundресурс не найден
409 Conflictконфликт состояния
422 Unprocessable Entityданные синтаксически верные, но не проходят правила
429 Too Many Requestsrate limit
500 Internal Server Errorошибка сервера

Не возвращайте 200 с текстом "error". Клиенты, прокси, мониторинг и тесты смотрят на статус-код.

Формат ошибок

Сделайте один формат ошибок для всего API:

{ "error": { "code": "validation_failed", "message": "Название книги обязательно", "fields": { "title": "required" } } }

message можно показать человеку. code удобно использовать в frontend-логике и тестах. fields помогает подсветить конкретные поля формы.

Не отдавайте пользователю внутренние ошибки базы данных:

pq: duplicate key value violates unique constraint ...

Лучше вернуть понятный ответ:

{ "error": { "code": "email_already_exists", "message": "Пользователь с таким email уже существует" } }

Пагинация

Списки должны иметь ограничение размера:

GET /books?limit=20

Для учебных проектов можно начать с page и limit:

GET /books?page=2&limit=20

Для больших данных лучше cursor pagination:

GET /books?limit=20&cursor=eyJpZCI6...

Ответ:

{ "items": [], "next_cursor": "eyJpZCI6..." }

Cursor pagination устойчивее, когда данные часто добавляются или удаляются.

Фильтры и сортировка

Фильтры обычно передают через query parameters:

GET /books?author_id=42&status=published&sort=-created_at

Хорошее правило: если параметр уточняет список, он идет в query string. Если параметр выбирает конкретный ресурс, он идет в path.

GET /books/42 GET /books?category=backend

Вложенные ресурсы

Если ресурс принадлежит другому, вложенный путь читается естественно:

GET /books/{book_id}/reviews POST /books/{book_id}/reviews

Но не делайте слишком глубокие цепочки:

/users/{user_id}/books/{book_id}/reviews/{review_id}/comments/{comment_id}

Чаще достаточно 1-2 уровней. Слишком глубокий URL обычно показывает, что границы ресурсов нужно пересмотреть.

Идемпотентность

Идемпотентная операция дает один и тот же итог при повторении. Например, DELETE /books/42 можно отправить два раза: после первого книга удалена, после второго она все еще удалена.

Это важно для retry. Если сеть оборвалась, клиент может не знать, дошел запрос или нет. Повторять безопаснее те операции, которые не создают дубликаты.

Для платежей, заказов и похожих операций часто используют idempotency key:

Idempotency-Key: 8a44a0f2-...

Сервер запоминает ключ и не выполняет одну и ту же операцию дважды.

CORS

CORS нужен браузеру, когда frontend и API находятся на разных origins. Например:

Frontend: http://localhost:3000 API: http://localhost:8080

Backend должен явно разрешить origin, методы и заголовки:

Access-Control-Allow-Origin: https://app.example.com Access-Control-Allow-Methods: GET, POST, PATCH, DELETE, OPTIONS Access-Control-Allow-Headers: Content-Type, Authorization

OpenAPI

OpenAPI-спецификация описывает эндпоинты, схемы запросов и ответов. Она полезна для:

  • документации;
  • генерации клиентов;
  • ревью API до реализации;
  • проверки контрактов;
  • синхронизации backend и frontend.

Минимальный фрагмент:

paths: /books: get: summary: List books responses: "200": description: Books list

Даже если вы не генерируете код, OpenAPI заставляет заранее договориться о форме API.

Версионирование

Если API публичный или им пользуются несколько клиентов, изменения нужно делать осторожно. Удаление поля, изменение типа или переименование ключа может сломать клиента.

Популярный вариант:

/api/v1/books

Не нужно вводить версии в маленьком учебном проекте без необходимости, но нужно понимать, что API - это контракт. Контракт нельзя менять незаметно.

Чеклист

  1. URL описывает ресурс.
  2. HTTP-метод описывает действие.
  3. GET не меняет состояние.
  4. Статус-коды соответствуют результату.
  5. Ошибки имеют единый JSON-формат.
  6. Списки имеют пагинацию.
  7. Фильтры находятся в query parameters.
  8. Вложенность URL не уходит глубже 1-2 уровней без причины.
  9. Опасные операции защищены от случайных повторов.
  10. API задокументирован через OpenAPI или понятное README.

Следующий шаг после статьи

Закрепите тему в реальном проекте и продолжайте обучение на курсах Praxis.

Продолжить изучение

Выбери следующую статью по маршруту или углубись в смежную тему.

Похожие статьи