REST поверх HTTP — архитектурный стиль, а не протокол
В этом курсе мы изучаем HTTP как сетевой протокол: байты, заголовки, версии. Но 90% случаев, когда вы используете HTTP в работе, — это вызовы API. И почти каждый разговор про API упирается в слово «REST». «Сделай мне REST API», «у нас REST endpoint», «это не REST, это RPC».
Этот урок — обзор REST с фокусом «что это вообще такое и при чём здесь HTTP». Глубоко мы REST не разбираем — для этого есть отдельный курс rest-api-fundamentals, где обсуждаются ресурсы, идентификация, версионирование, pagination, HATEOAS, ошибки, security, и десятки других тем дизайна API. Здесь же мы посмотрим на REST с network-perspective: как идиоматичный REST использует HTTP-методы, статус-коды, заголовки — то, что вы уже знаете из этого модуля.
Если ваша работа — интеграции с API, ETL из чужих endpoint’ов, проектирование собственных сервисов — идите в rest-api-fundamentals обязательно. Здесь — быстрый overview, чтобы вы понимали, как HTTP используется в API-мире.
Что такое REST на самом деле
REST — это архитектурный стиль, описанный Роем Филдингом в его диссертации 2000 года. Не протокол, не спецификация, не RFC — набор архитектурных принципов (constraints), которые в совокупности дают желаемые свойства распределённой системы.
Из всех constraints на практике людей волнует один — uniform interface. Из этого constraint следует, что REST API:
- Ресурс-ориентирован. Каждый URL обозначает ресурс.
/users/42— пользователь 42,/orders/100/items— список items заказа 100. - Использует HTTP-методы как глаголы. GET читает, POST создаёт, PUT/PATCH обновляет, DELETE удаляет.
- Использует статус-коды по семантике. 200 OK, 201 Created, 404 Not Found, 422 Unprocessable Entity.
- Возвращает медиа-типы. JSON, XML, иногда HTML. С Content-Type.
Идиоматичный REST: пример CRUD для users
Возьмём API управления пользователями. Идиоматичный REST это:
# Получить список пользователей
GET /users HTTP/1.1
Accept: application/json
# Ответ
HTTP/1.1 200 OK
Content-Type: application/json
ETag: "list-version-42"
{"users": [{"id": 1, ...}, {"id": 2, ...}], "total": 100}
# Получить одного пользователя
GET /users/42 HTTP/1.1
Accept: application/json
# Ответ
HTTP/1.1 200 OK
Content-Type: application/json
ETag: "user-42-v3"
{"id": 42, "name": "Linus", "email": "[email protected]"}
# Создать нового пользователя
POST /users HTTP/1.1
Content-Type: application/json
{"name": "Ada", "email": "[email protected]"}
# Ответ
HTTP/1.1 201 Created
Location: /users/43
Content-Type: application/json
{"id": 43, "name": "Ada", "email": "[email protected]"}
# Полностью заменить пользователя
PUT /users/42 HTTP/1.1
Content-Type: application/json
If-Match: "user-42-v3"
{"id": 42, "name": "Linus B Torvalds", "email": "[email protected]"}
# Ответ
HTTP/1.1 200 OK
ETag: "user-42-v4"
Content-Type: application/json
{"id": 42, "name": "Linus B Torvalds", ...}
# Частичное обновление
PATCH /users/42 HTTP/1.1
Content-Type: application/json
{"name": "Linus Benedict Torvalds"}
# Ответ
HTTP/1.1 200 OK
ETag: "user-42-v5"
{"id": 42, ...}
# Удалить
DELETE /users/42 HTTP/1.1
# Ответ
HTTP/1.1 204 No Content
Заметьте паттерн: URL обозначает ресурс, метод — действие. URL никогда не содержит глаголы (/users/42/delete — НЕ REST; DELETE /users/42 — REST).
Статус-коды в REST: правильные коды для правильных ситуаций
Идиоматичный REST API использует широкую палитру статус-кодов:
Полезные тонкости:
- 201 Created vs 200 OK для POST. Если POST создал ресурс — 201, с Location header. Если POST выполнил действие без создания (например, /users/42/send-email) — 200 OK или 204.
- 404 vs 410. 404 — может не существует, может вы не знаете. 410 — точно когда-то существовало, но удалено.
- 404 vs 403. Некоторые системы возвращают 404 вместо 403 — чтобы атакующий не мог даже узнать о существовании ресурса.
Глубже про дизайн ошибок и pagination — в rest-api-fundamentals.
REST vs RPC: разница
RPC (Remote Procedure Call) — альтернативный стиль. Вместо ресурсов и методов — функции:
REST: RPC:
GET /users/42 POST /getUser {"id": 42}
POST /users POST /createUser {...}
DELETE /users/42 POST /deleteUser {"id": 42}
В RPC URL — это название функции. Метод обычно всегда POST (потому что в RPC мысленная модель — «вызов функции с параметрами»).
Что лучше? Зависит от случая:
- REST: натурален для CRUD-операций над сущностями. Хорош для public APIs (Stripe, GitHub).
- RPC: натурален для actions, которые не лезут в CRUD-модель. «Запусти задачу», «отмени операцию», «синхронизируй».
- gRPC: современная реализация RPC поверх HTTP/2 с бинарным форматом (protobuf). Очень быстро, типизировано, отлично для internal services.
В реальности почти все «REST APIs» — это гибрид. PayPal, Stripe — ресурсо-ориентированные, но имеют action-эндпоинты типа POST /charges/{id}/refund. Это нормально и прагматично.
Ресурсы и URI: какие хорошие, какие плохие
Дизайн URI — большая тема, тут только базовые правила:
Правила:
- Существительные для ресурсов во множественном числе:
/users,/orders,/products. - ID в path для конкретного ресурса:
/users/42. - Глагол — HTTP метод, а не URL.
- Nested для вложенных ресурсов:
/users/42/orders. - Query parameters для фильтров/пагинации:
/orders?status=paid&page=2. - Lowercase, kebab-case для multi-word:
/user-profiles, не/UserProfiles.
Подробно про дизайн — курс rest-api-fundamentals.
Где REST не идеален
REST — хороший дефолт, но не серебряная пуля.
-
Сложные queries. Получить «users старше 25, у которых есть orders за последний месяц, с количеством orders > 5» — сложно выразить через ресурсы. Тут лучше GraphQL или специальный search endpoint.
-
Real-time data. REST — request/response. Для уведомлений или live feed — WebSocket, SSE, gRPC streaming.
-
Большое количество round-trips. Если для UI нужно 20 разных ресурсов — 20 HTTP-запросов. GraphQL умеет одним запросом.
-
Внутри microservices. Между сервисами в data center REST с JSON — не самый эффективный. gRPC с protobuf быстрее и более типизирован.
В реальной архитектуре часто гибрид: REST для external public API + gRPC / GraphQL внутри + WebSocket для real-time.
Реальный пример: GitHub API
GitHub API — хороший пример идиоматичного REST. Несколько примеров:
# Получить пользователя
curl https://api.github.com/users/torvalds
# Получить репозиторий
curl https://api.github.com/repos/torvalds/linux
# Список звёзд репо
curl https://api.github.com/repos/torvalds/linux/stargazers
# Создать gist (нужна аутентификация)
curl -X POST https://api.github.com/gists \
-H "Authorization: Bearer ghp_xxx" \
-H "Content-Type: application/json" \
-d '{"description": "test", "files": {"a.txt": {"content": "hello"}}, "public": true}'
# Обновить gist
curl -X PATCH https://api.github.com/gists/{id} \
-H "Authorization: Bearer ghp_xxx" \
-d '{"description": "updated"}'
# Удалить gist
curl -X DELETE https://api.github.com/gists/{id} \
-H "Authorization: Bearer ghp_xxx"
Обратите внимание:
- URL — ресурсы во множественном числе (
/users,/repos,/gists). - ID или name в path.
- Методы соответствуют действиям.
- Статус-коды семантичны: 200 OK при GET, 201 Created при POST успешной, 404 Not Found для несуществующих, 403 для no permission.
- ETag присутствует — можно делать conditional requests.
Попробуй сам
# 1. Изучить GitHub API -- классический REST
curl -v https://api.github.com/users/torvalds 2>&1 | grep -E '^[<>]' | head -30
# 2. Посмотреть на ETag и conditional request
ETAG=$(curl -sI https://api.github.com/users/torvalds | grep -i etag | awk '{print $2}' | tr -d '\r')
echo "ETag: $ETAG"
curl -v -H "If-None-Match: $ETAG" https://api.github.com/users/torvalds 2>&1 | grep -E '^[<>]' | head -10
# Должно быть 304 Not Modified
# 3. Попробуй разные статус-коды через httpbin
curl -v https://httpbin.org/status/201 2>&1 | grep '^<'
curl -v https://httpbin.org/status/422 2>&1 | grep '^<'
curl -v https://httpbin.org/status/429 2>&1 | grep '^<'
# 4. POST с JSON -- посмотреть, как сервер парсит
curl -v -X POST https://httpbin.org/anything \
-H "Content-Type: application/json" \
-d '{"resource": "user", "action": "create"}' 2>&1 | tail -20
# 5. Find a public REST API на https://github.com/public-apis/public-apis
# Например, открытое API JSONPlaceholder
curl https://jsonplaceholder.typicode.com/posts/1
curl https://jsonplaceholder.typicode.com/users/1
curl -X POST -H "Content-Type: application/json" \
-d '{"title": "test"}' \
https://jsonplaceholder.typicode.com/posts
Куда дальше — rest-api-fundamentals
Этот урок был быстрым обзором REST с network-perspective. Если ваша работа — API (интеграции, проектирование, миграции, документация), обязательно пройдите курс rest-api-fundamentals. Там разбирается:
- Глубокий дизайн URL и ресурсов (nested, polymorphic, hierarchical)
- Pagination — cursor vs offset, edge cases
- Versioning — /v1/, header-based, content negotiation
- Error format — RFC 7807 Problem Details, какие поля давать, как структурировать
- Authentication patterns — Basic, Bearer, OAuth 2, OIDC, API keys
- Rate limiting — token bucket, sliding window, как сервер шлёт headers
- HATEOAS — что это и зачем
- Webhooks — callbacks, как принимать, security (HMAC signatures)
- OpenAPI / Swagger — документация и codegen
- GraphQL и gRPC — альтернативы и trade-offs
Здесь, в networking-fundamentals, мы концентрируемся на сетевом стэке. Дальше идём в TLS, sockets, балансировку.
Что вы должны вынести
- REST — архитектурный стиль, не протокол.
- Ресурс-ориентированность: URL = noun, метод = verb.
- Идиоматичный REST использует широкий спектр HTTP status codes по семантике.
- REST vs RPC vs GraphQL — разные стили под разные задачи. Часто гибрид.
- URI design: множественные существительные, ID в path, kebab-case, нет глаголов.
- Conditional requests + ETag прекрасно работают с REST для concurrency control.
- Глубже про дизайн — курс rest-api-fundamentals.
Полный курс REST API: дизайн, pagination, versioning, security, OpenAPI