REST API — общение фронтенда с бэкендом

REST — архитектурный стиль проектирования API поверх HTTP. Клиент (SPA на Vue/React, мобильное приложение) делает запросы вида «GET /users/42», а сервер отвечает JSON-объектом. В этом курсе — синтаксис HTTP-взаимодействия и правила хорошего API.

Уровень: средний 18 разделов ⏱ 2–3 часа

1. Клиент-серверная модель

Приложение делится на две части:

API — «контракт» между ними: набор URL и методов, по которым можно получать и менять данные без знания внутренностей сервера.

Серверная часть в деталях — в курсах FastAPI (Python), Spring Boot (Java), Flask.

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 Error
Клиент реагирует на 4xx иначе, чем на 5xx: 4xx — «исправь запрос», 5xx — «это на стороне сервера, попробуй позже».

4. Ресурсы и именование

Хорошее REST API строит маршруты вокруг существительных:

Термин «RESTful» толкуют по-разному. Главное, что важно на практике: предсказуемые URL, верные методы и статусы, понятный JSON.

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: /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…
Токен в localStorage — удобно, но уязвимо для XSS. Современная практика: httpOnly cookie или in-memory + refresh. Храни токен аккуратно и учти это в Best Practices.

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: OK

11. Postman — инструмент для ручных запросов

Postman (или Insomnia, или BRuno) позволяет хранить коллекции запросов и переменные окружения:

Та же работа частично делается из Chrome DevTools (Network) и CLI (curl). Postman удобен для батареи ручных проверок и быстрых сценариев без UI.

12. Фильтрация, пагинация и сортировка

Списки ресурсов быстро разрастаются. Хороший REST API ограничивает размер ответа и даёт клиенту гибкие инструменты выборки через query-параметры.

GET /products?page=2&limit=20
GET /products?category=phone&sort=price&order=asc
GET /products?filter=inStock:true
ПодходКогда брать
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, сервер запоминает результат первого запроса и отдаёт его при повторах.

Без идемпотентности повторный клик «Оплатить» из-за сетевой ошибки может списать деньги дважды. Ключ идемпотентности — стандарт индустрии, а не роскошь.

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/HTTPPlaywright, Selenium
Тесты — часть API-контракта: запускайте их в CI на каждый коммит, а документацию (OpenAPI) генерируйте из кода, чтобы схема и реализация не расходились.

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
Не разрешайте CORS для всех доменов без необходимости: чужой сайт сможет вызывать ваш API от имени пользователя. Отдавайте явный Access-Control-Allow-Origin из белого списка доменов.
Храните CORS-конфигурацию в деплой-конфиге, а не зашивайте в код: в dev разрешите все домены, в проде — только свои.

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не отдавать устаревшее без проверки
Не кэшируйте ответы с токенами и личными данными: ни CDN, ни браузер не должны хранить чужие профили. Для таких эндпоинтов — Cache-Control: no-store.
ETag генерируйте от содержимого (md5 тела), а не от даты — иначе состояние меняется в одном клиенте, а новый получает устаревший вариант.

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
Event-очередь на стороне сервера (Redis Streams, Kafka) делает доставку надёжнее: приложение отвечает 200 сразу, а отправка идёт из воркера.
Webhook-эндпоинт клиента не должен быть публично «глупым»: принимайте только подписанные события с известного домена и ограничивайте частоту.

18. GraphQL, gRPC или REST: выбор инструмента

REST — не единственный способ общения клиента и сервера. GraphQL позволяет клиенту запросить ровно те поля, что нужны, одним POST к единственному эндпоинту. gRPC использует бинарные сообщения и HTTP/2 со стримами — лидер по скорости внутри микросервисов.

КритерийRESTGraphQLgRPC
ФорматJSONJSON-запрос, JSON-ответProtobuf (бинарный)
ПередачаHTTP/1.1HTTP/1.1–2HTTP/2, стримы
Для когопубличные APIвнешние клиенты с гибкой выборкойсервис-сервис
query { product(id: 7) { name price { value currency } } }

GraphQL побеждает, когда клиентов много и у всех разный набор полей. gRPC — когда нужны скорость и стриминг. REST удобен простотой, кэшированием и совместимостью с любым инструментом.

Не переделывайте всё на GraphQL «ради моды»: для CRUD-админки он добавит сложности без выгоды. Гибрид тоже норм: внешний REST, внутренний gRPC.
Для gRPC нужен генератор кода на обоих концах (proto-файлы), а браузерные клиенты требуют gRPC-Web — учитывайте это при выборе.

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-схемеранняя разработка
Моки не проверяют ни клиент-серверное поведение, ни сеть. Это инструмент разработки, а не замена интеграционным тестам.
См. также курс Selenium — тесты с моками работают быстро и не зависят от внешних сервисов.

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
Шлюз сам становится точкой отказа и единым местом для ложных взлётов — держите его конфигурацию в git и покрывайте маршруты тестами.

21. Наблюдаемость API: логи, метрики, трейсы

Как понять, что API деградирует, ещё до жалоб пользователей? Три компонента observability: логи (что произошло), метрики (сколько и как часто) и трейсы (где именно).

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 публичном, тем дороже каждый ломающий релиз. Планируйте схемы заранее, а при сомнении — новый поля вместо переиспользования чужих.

Что дальше

Опробуй запросы к реальному API из React/Vue 3, ответы типизируй в TypeScript. Серверную часть собери на FastAPI, покрытие — Vitest, деплой — Docker.

← Вернуться к курсам