REST API — общение фронтенда с бэкендом
REST — архитектурный стиль проектирования API поверх HTTP. Клиент (SPA на Vue/React, мобильное приложение) делает запросы вида «GET /users/42», а сервер отвечает JSON-объектом. В этом курсе — синтаксис HTTP-взаимодействия и правила хорошего API.
1. Клиент-серверная модель
Приложение делится на две части:
- Клиент — браузер, мобильное приложение (курсы React, Vue 3);
- Сервер — FastAPI/Spring/Django, со своей логикой и базой данных.
API — «контракт» между ними: набор URL и методов, по которым можно получать и менять данные без знания внутренностей сервера.
2. HTTP-методы: CRUD
Каждое действие над ресурсом — свой метод:
| Метод | Действие | Пример |
|---|---|---|
| GET | читать (без «побочек») | GET /products |
| POST | создать ресурс | POST /products |
| PUT | полностью заменить | PUT /products/7 |
| PATCH | частично обновить | PATCH /products/7 |
| DELETE | удалить | DELETE /products/7 |
GET /products?page=2&limit=20 # список с пагинацией
GET /products/7 # один товар
POST /products # создать (тело в body)
PUT /products/7 # заменить целиком
DELETE /products/7 # удалить3. Статус-коды
2xx — успех
200 OK # стандартный ответ
201 Created # ресурс создан (POST)
204 No Content # удаление без тела
3xx — редиректы
301 Moved Permanently
4xx — ошибка клиента
400 Bad Request # битый запрос/валидация
401 Unauthorized # нет авторизации
403 Forbidden # нет прав
404 Not Found # ресурс не найден
409 Conflict # конфликт состояния
422 Unprocessable # валидация данных
5xx — ошибка сервера
500 Internal Server Error4. Ресурсы и именование
Хорошее REST API строит маршруты вокруг существительных:
- используй множественное число:
/users, а не/user; - вложенность — только для явной связи:
/users/5/orders; - никаких глаголов в URL:
DELETE /orders/9, а не/orders/cancel; - нижний регистр, дефисы; цифры и кривые юникоды — только при необходимости.
5. Запросы: query, headers, body
// GET с фильтрами и пагинацией
GET /products?category=phone&sort=price&order=asc&page=2
// заголовки
Accept: application/json
Authorization: Bearer eyJhbGciOi…
// тело JSON-запроса
POST /products
Content-Type: application/json
{
"title": "Телефон",
"price": 19990,
"inStock": true
}Принцип: GET нельзя менять данные, тела в GET быть не должно, а безопасные запросы должны быть идемпотентными (GET/PUT/DELETE можно повторять без риска).
6. Работа с API через fetch
// get
const res = await fetch("/api/products?limit=5");
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const products = await res.json();
// create
await fetch("/api/products", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ title: "Телефон", price: 19990 })
});
// delete
await fetch(`/api/products/${id}`, { method: "DELETE" });// на Vue часто оборачивают в composable-обёртку (см. курс Vue 3)
// обработка ошибок
try {
const data = await api.get("/users");
} catch (err) {
console.error("API error:", err);
}7. Версионирование
API эволюционирует; старые клиенты не должны ломаться. Способы:
- в пути:
/api/v1/users,/api/v2/users; - через заголовок:
Accept: application/vnd.mysite.v2+json.
/api/v1/. Плавный период поддержки старой версии — 6–12 месяцев.8. Авторизация: токены и JWT
Типовой сценарий: пользователь логинится → получает JWT (JSON Web Token) → прикладывает его к каждому запросу:
POST /auth/login
{ "email": "a@site.ru", "password": "secret" }
// ответ: access_token (10-15 мин) и refresh_token (дней)
{ "access_token": "eyJhbGciOi…", "token_type": "bearer" }
GET /me
Authorization: Bearer eyJhbGciOi…9. Ошибки и валидация
Возвращай ошибки единообразно, чтобы клиент мог их парсить:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Некорректное поле price",
"details": { "price": "должно быть числом > 0" }
}
}- не отдавай стек-трейсы наружу;
- сообщения на языке пользователя (или кодовые + перевод на клиенте);
- всегда валидируй вход на сервере, не только на клиенте.
10. Документация: OpenAPI и Swagger
OpenAPI — спецификация API в YAML/JSON. По ней автоматически генерируется интерактивная документация (Swagger UI). В FastAPI документация появляется «бесплатно» по адресу /docs.
openapi: 3.0.0
info:
title: Products API
version: 1.0.0
paths:
/products:
get:
summary: Список товаров
parameters:
- name: limit
in: query
schema: { type: integer }
responses:
"200":
description: OK11. Postman — инструмент для ручных запросов
Postman (или Insomnia, или BRuno) позволяет хранить коллекции запросов и переменные окружения:
- тестировать GET/POST с заголовками и телами без UI;
- цепочки запросов с автоматической подстановкой токена;
- генерировать код запроса в разных языках (Python requests, fetch JS).
12. Фильтрация, пагинация и сортировка
Списки ресурсов быстро разрастаются. Хороший REST API ограничивает размер ответа и даёт клиенту гибкие инструменты выборки через query-параметры.
GET /products?page=2&limit=20
GET /products?category=phone&sort=price&order=asc
GET /products?filter=inStock:truepage/limit— классическая пагинация;cursor/offset— для больших и меняющихся наборов;- имена фильтров лучше держать стабильными и документированными, чтобы клиенты не зависели от внутренней модели.
| Подход | Когда брать |
|---|---|
| page+limit | обычные списки в админках и каталогах |
| cursor | ленты, логи, бесконечные списки |
| сортировка | каталоги с ценами, датами, рейтингом |
total, page, has_next. Это экономит миллионы лишних запросов к счётчикам.13. Rate limiting и идемпотентность
Публичный API должен защищаться от перегрузок и повторных запросов. Rate limiting ограничивает число запросов от клиента, а идемпотентность гарантирует, что повторная отправка одного запроса не создаст второго заказа.
HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 99Схема идемпотентности проста: клиент присылает заголовок Idempotency-Key, сервер запоминает результат первого запроса и отдаёт его при повторах.
- лимиты обычно считаются на ключ приложения, IP или id аккаунта;
- лимит не обязан быть одинаковым для всех эндпоинтов: тяжёлый экспорт — строже;
- идемпотентность обязательна для платежей, заказов и переводов.
14. Тестирование API
Качество API проверяется тестами. Юнит-тесты покрывают бизнес-логику сервисов, а интеграционные — контракты HTTP-эндпоинтов без реального подключения к внешним системам.
// pytest + FastAPI TestClient
from fastapi.testclient import TestClient
client = TestClient(app)
def test_get_product():
resp = client.get("/products/1")
assert resp.status_code == 200
assert resp.json()["id"] == 1Соединения к сторонним базам и сервисам заменяются моками: тест становится быстрым и детерминированным. Дополнительно контрактное тестирование (например, Pact) проверяет совместимость клиента и сервера.
| Уровень | Что проверяет | Инструменты |
|---|---|---|
| модульный | логика и правила | pytest, Vitest, JUnit |
| интеграционный | эндпоинты и контракты | TestClient, MockMvc, Supertest |
| e2e | сценарии через UI/HTTP | Playwright, Selenium |
15. CORS и безопасность API
Браузер блокирует запросы API с другого домена — это политика same-origin. Для разрешения кросс-доменных запросов сервер отдаёт CORS-заголовки, а браузер выполняет preflight (OPTIONS) для сложных запросов с кастомными заголовками.
Access-Control-Allow-Origin: https://shop.example.ru
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true| Заголовок | Назначение |
|---|---|
| Strict-Transport-Security | принудительный HTTPS (HSTS) |
| Content-Security-Policy | ограничение источников скриптов и стилей |
| X-Content-Type-Options: nosniff | запрет подмены MIME-типа |
| Referrer-Policy | контроль передачи Referer |
Access-Control-Allow-Origin из белого списка доменов.16. HTTP-кэширование: ETag и Cache-Control
Помимо кэша в Redis, REST API использует кэши на уровне HTTP: браузер, CDN и сетевые прокси могут отдавать ответы без обращения к серверу. Управляется всё заголовками ответа.
Cache-Control: public, max-age=3600
ETag: "v1-5f7a2b9"
# клиент при повторном запросе шлёт
# If-None-Match: "v1-5f7a2b9"
# сервер отвечает 304 Not Modified без тела| Директива | Значение |
|---|---|
| max-age | свежесть ответа в секундах |
| no-cache | кэшировать, но каждый раз проверять валидацию |
| no-store | не кэшировать вовсе |
| must-revalidate | не отдавать устаревшее без проверки |
- кэшируйте только безопасные GET-ответы без персональных данных;
- для вариативных ответов добавляйте
Vary: Accept-Language; - для ассетов — долгий max-age и хеш в имени файла.
Cache-Control: no-store.17. Webhooks и асинхронные сценарии
Если результат операции готов через минуты (платёж, обработка видео), API не держит запрос открытым: клиент отправляет задачу, сервер отвечает «принято», а результат приходит позже. Два способа: polling (клиент опрашивает статус) и webhook (сервер сам вызывает URL клиента).
# сервер отправляет POST на URL клиента:
POST https://client.example.ru/webhook
{ "event": "payment.succeeded", "order_id": 123 }
X-Signature: sha256=4f6a92...
# при повторе доставки idempotency-ключ события:
X-Idempotency-Key: payment-123-2026- подписывайте полезную нагрузку HMAC-секретом: клиент сверяет сигнатуру, иначе подделанные события;
- настройте retry с экспоненциальной задержкой и dead-letter для необработанных;
- обработчик должен быть идемпотентным: повторная доставка не создаёт второй платёж.
18. GraphQL, gRPC или REST: выбор инструмента
REST — не единственный способ общения клиента и сервера. GraphQL позволяет клиенту запросить ровно те поля, что нужны, одним POST к единственному эндпоинту. gRPC использует бинарные сообщения и HTTP/2 со стримами — лидер по скорости внутри микросервисов.
| Критерий | REST | GraphQL | gRPC |
|---|---|---|---|
| Формат | JSON | JSON-запрос, JSON-ответ | Protobuf (бинарный) |
| Передача | HTTP/1.1 | HTTP/1.1–2 | HTTP/2, стримы |
| Для кого | публичные API | внешние клиенты с гибкой выборкой | сервис-сервис |
query { product(id: 7) { name price { value currency } } }GraphQL побеждает, когда клиентов много и у всех разный набор полей. gRPC — когда нужны скорость и стриминг. REST удобен простотой, кэшированием и совместимостью с любым инструментом.
19. Моки и стабы: API без бэкенда
Разработку фронтенда не стоит блокировать готовностью API. Мок-сервер возвращает согласованные ответы по OpenAPI-схеме — фронт работает, пока бэкенд ещё не готов, а тесты получают детерминированные данные.
# MSW — Mock Service Worker
import { http, HttpResponse } from 'msw';
export const handlers = [
http.get('/api/users/:id', () =>
HttpResponse.json({ id: 7, name: 'Anna' })),
];| Инструмент | Уровень перехвата | Где удобно |
|---|---|---|
| MSW | сеть (Service Worker) | браузер и тесты |
| json-server | отдельный процесс | быстрый прототип |
| oas-mock | по OpenAPI-схеме | ранняя разработка |
- моки должны повторять форму реальных ответов — иначе интеграция упадёт на первом же продакшне;
- генерируйте моки из схемы, чтобы типы не расходились;
- в тестах мок подменяете точечно, а в дев-окружении — весь API.
20. API Gateway и паттерн BFF
Когда сервисов несколько, клиенту не нужно знать о каждом из них. API Gateway — единая точка входа: маршрутизация, аутентификация, лимиты, кэш и агрегация ответов в одном шлюзе.
GET /profile # клиент отправляет один запрос
→ gateway: /users/me (сервис пользователей)
→ gateway: /orders?user=me (сервис заказов)
→ response: объединённый JSONЧастный случай шлюза — BFF (Backend for Frontend): отдельный тонкий слой под конкретный интерфейс. Веб-сайт получает один компактный ответ, мобильное приложение — свой, с меньшим объёмом полей.
| Подход | Кому подходит |
|---|---|
| Gateway | много микросервисов, общие требования (auth, лимиты) |
| BFF | несколько клиентов с разными потребностями |
| Ничего | маленькое монолитное API |
21. Наблюдаемость API: логи, метрики, трейсы
Как понять, что API деградирует, ещё до жалоб пользователей? Три компонента observability: логи (что произошло), метрики (сколько и как часто) и трейсы (где именно).
- Логи: метод, путь, статус, длительность, correlation-id — и всё в структурированном JSON, чтобы фильтровать машиной.
- Метрики: latency (p50/p95/p99), error rate, throughput по эндпоинтам — считает Prometheus, рисует Grafana.
- Трейсы: идентификатор запроса прокидывается в заголовках через сервисы, каждый шаг помечается спанами.
X-Request-Id: 8f2c6d9e-3a51-4bf0-b1d4-75a2c01e8700Прод расширения из курса Spring Boot: эндпоинт /actuator/metrics уже отдаёт данные для scrape. Хорошая практика — SLA-панель: «99.9% ответов < 300 мс» и алерт при превышении.
22. Эволюция API без ломающих изменений
API живёт дольше версии продукта, поэтому его развитие — отдельная дисциплина. Главное правило: не ломать клиентов. Есть легальные способы расширять интерфейс.
- добавляйте поля — удалять и менять смысл нельзя;
- значения по умолчанию подбирайте так, чтобы старое поведение сохранялось;
- ломку объявляйте заранее и через промежуточные шаги.
Процесс деприкации: пометить поле как устаревшее → оповестить потребителей → отвечать с заголовком Deprecation → удалить через N месяцев. Заголовок Sunset: Fri, 20 Feb 2027 12:00:00 GMT сообщает клиенту точную дату отключения.
Deprecation: true
Sunset: Fri, 20 Feb 2027 12:00:00 GMT| Изменение | Совместимость |
|---|---|
| новое поле, новый эндпоинт | обратная совместимость |
| новый обязательный параметр | ломающее |
| переименование поля | ломающее (те же данные нужно переименовывать) |
Что дальше
Опробуй запросы к реальному API из React/Vue 3, ответы типизируй в TypeScript. Серверную часть собери на FastAPI, покрытие — Vitest, деплой — Docker.