Перейти к содержанию
Бэкенд и системы

36 вопросов по теме «HTTP и REST API» на собеседовании

В этом материале — 36 вопросов из русской колоды RecallDeck по теме «HTTP и REST API». Сначала сформулируйте короткий ответ сами, затем откройте подробный разбор и проверьте примеры, ограничения и отказные случаи.

29 мин чтения36 подробных ответовПроверено 24 августа 2026
Главная мысль

Стройте ответ вокруг владения состоянием, лимитов, отказа и восстановления. Определение становится инженерным ответом только в конкретном production-сценарии.

Вопросы и ответы

36 подробных ответов

01

1. Что такое HTTP и как устроен цикл запрос-ответ?

Короткий ответ: HTTP (HyperText Transfer Protocol) — текстовый прикладной протокол клиент-сервер поверх TCP (в HTTP/3 — поверх QUIC/UDP). Клиент отправляет запрос, сервер возвращает ответ; соединение без сохранения состояния между запросами (stateless).

Подробно:

Запрос состоит из стартовой строки (метод + путь + версия), заголовков и опционального тела:

POST /api/v1/users HTTP/1.1
Host: api.example.com
Content-Type: application/json
Accept: application/json
Content-Length: 38

{"name": "Анна", "email": "a@ex.com"}

Ответ состоит из строки статуса (версия + код + reason phrase), заголовков и тела:

HTTP/1.1 201 Created
Content-Type: application/json
Location: /api/v1/users/42

{"id": 42, "name": "Анна", "email": "a@ex.com"}

⚠️ Ловушка: «HTTP работает только поверх TCP» — неверно для HTTP/3, который использует QUIC поверх UDP. И reason phrase («OK», «Created») носит чисто информативный характер — клиенты должны опираться на числовой код, а не на текст.

02

2. HTTP-методы: семантика, safe и идемпотентные

Короткий ответ: Метод задаёт намерение операции над ресурсом. Safe — не меняет состояние сервера (только чтение). Idempotent — повторный одинаковый запрос даёт тот же эффект, что и одиночный.

Подробно:

Метод Назначение Safe Idempotent Тело запроса Тело ответа
GET Получить ресурс нет да
HEAD Как GET, но только заголовки нет нет
OPTIONS Узнать допустимые методы / CORS нет да
POST Создать / выполнить действие да да
PUT Полностью заменить / создать по URI да да
PATCH Частично изменить ресурс ❌* да да
DELETE Удалить ресурс опц. опц.

* PATCH идемпотентен не по спецификации, но может быть таковым при правильном проектировании (например, замена полей абсолютными значениями вместо инкрементов).

  • Safe-методы (GET, HEAD, OPTIONS) можно кэшировать, префетчить, повторять без последствий.
  • Idempotent-методы (GET, HEAD, OPTIONS, PUT, DELETE) безопасно ретраить при сетевых сбоях.
  • HEAD полезен, чтобы проверить наличие ресурса или его размер (Content-Length) без скачивания тела.
  • OPTIONS используется браузером для CORS-preflight.

⚠️ Ловушка: «GET всегда без тела» — спецификация HTTP/1.1 разрешает тело у GET, но его поведение не определено, многие прокси и серверы его игнорируют или режут соединение. Не передавайте параметры в теле GET. Также: safe не равно idempotent — DELETE идемпотентен, но не безопасен.

03

3. В чём разница между POST, PUT и PATCH?

Короткий ответ: POST создаёт ресурс (сервер назначает URI) и не идемпотентен; PUT полностью заменяет ресурс по известному URI и идемпотентен; PATCH частично обновляет ресурс.

Подробно:

POST — создание подчинённого ресурса в коллекции, URI назначает сервер:

POST /api/v1/articles HTTP/1.1
Content-Type: application/json

{"title": "REST", "body": "..."}
HTTP/1.1 201 Created
Location: /api/v1/articles/777

Два одинаковых POST создадут две статьи — не идемпотентно.

PUT — клиент знает URI и присылает полное представление; ресурс создаётся или заменяется целиком:

PUT /api/v1/articles/777 HTTP/1.1
Content-Type: application/json

{"title": "REST v2", "body": "новый текст", "tags": []}

Повторный идентичный PUT оставит ресурс в том же состоянии — идемпотентно. Важно: поля, не указанные в теле, считаются обнулёнными/удалёнными (полная замена).

PATCH — частичное изменение, передаются только меняемые поля:

PATCH /api/v1/articles/777 HTTP/1.1
Content-Type: application/json

{"title": "REST v3"}

Поле body остаётся прежним. Существует формализованный application/json-patch+json (RFC 6902) с операциями add/remove/replace.

⚠️ Ловушка: частая ошибка — использовать PUT для частичного обновления. Если PUT принимает частичное тело и не зануляет остальные поля, он семантически превращается в PATCH и нарушает контракт. Если клиент шлёт PUT без поля — оно должно исчезнуть, иначе это нарушение семантики.

04

4. Что такое идемпотентность и зачем она нужна?

Короткий ответ: Операция идемпотентна, если выполнение её N раз даёт тот же эффект на состоянии сервера, что и одно выполнение. Это позволяет безопасно повторять запросы при сетевых сбоях.

Подробно:

Идемпотентность касается эффекта на сервере, а не идентичности ответов. Например, два DELETE одного ресурса: первый вернёт 204, второй — 404, но состояние сервера (ресурс отсутствует) одинаково — это идемпотентно.

Идемпотентные методы: GET, HEAD, OPTIONS, PUT, DELETE. Не идемпотентен по умолчанию: POST (создаёт новую сущность при каждом вызове), PATCH (зависит от семантики — инкремент balance += 10 не идемпотентен, замена balance = 100 идемпотентна).

Зачем: при таймауте клиент не знает, дошёл запрос или нет. Идемпотентный запрос можно безопасно повторить. Неидемпотентный (POST оплаты) повторять опасно — можно создать дубликат.

⚠️ Ловушка: идемпотентность не про «возвращается одинаковый ответ». Счётчик просмотров, реализованный на GET (GET /article?inc_views=1), нарушает контракт: GET должен быть safe и idempotent, а тут он меняет состояние. Поисковики и префетч-агенты «накрутят» просмотры.

05

5. Idempotency key для платежей

Короткий ответ: Idempotency-Key — уникальный идентификатор, который клиент передаёт в заголовке, чтобы сервер распознал повтор неидемпотентного запроса (например POST оплаты) и не выполнил операцию дважды.

Подробно:

POST создаёт ресурс — он не идемпотентен. Но при платежах ретрай после таймаута не должен списывать деньги дважды. Решение — клиент генерирует уникальный ключ (UUID) и шлёт его:

POST /api/v1/payments HTTP/1.1
Idempotency-Key: 7c4e1f2a-1d3b-4f5a-9c8e-6b2a1d0e3f4c
Content-Type: application/json

{"amount": 5000, "currency": "RUB", "order_id": "A-123"}

Логика сервера:

  1. При первом запросе с этим ключом — выполнить операцию, сохранить (ключ → результат + статус) в хранилище (например Redis с TTL).
  2. При повторе с тем же ключом — не выполнять снова, а вернуть сохранённый результат (обычно тот же 201 и то же тело).
  3. Если первый запрос ещё в процессе (in-flight) — вернуть 409 Conflict или подождать.
  4. Если тело при повторе отличается от исходного — вернуть 422 (ключ переиспользован с другими данными).

Так делают Stripe, PayPal, YooKassa. Ключ хранят с TTL (например 24 часа).

⚠️ Ловушка: ключ должен генерировать клиент (до отправки), а не сервер, иначе при ретрае сгенерируется новый ключ и защита не сработает. Также важна атомарность: проверка «есть ли ключ» и запись должны быть транзакционными (через INSERT ... ON CONFLICT или Redis SETNX), иначе две параллельные попытки оба пройдут проверку и спишут дважды.

06

6. Коды статусов HTTP: классы 1xx-5xx

Короткий ответ: Первая цифра кода задаёт класс: 1xx — информационные, 2xx — успех, 3xx — перенаправление, 4xx — ошибка клиента, 5xx — ошибка сервера.

Подробно:

  • 1xx Informational — промежуточные. 100 Continue (можно слать тело), 101 Switching Protocols (апгрейд до WebSocket).
  • 2xx Success — 200 OK, 201 Created, 202 Accepted (принято на асинхронную обработку), 204 No Content, 206 Partial Content (range-запросы).
  • 3xx Redirection — 301 Moved Permanently, 302 Found, 303 See Other, 304 Not Modified, 307/308 (сохраняют метод).
  • 4xx Client Error — 400, 401, 403, 404, 405 Method Not Allowed, 409 Conflict, 410 Gone, 422 Unprocessable Entity, 429 Too Many Requests.
  • 5xx Server Error — 500 Internal Server Error, 501 Not Implemented, 502 Bad Gateway, 503 Service Unavailable, 504 Gateway Timeout.

⚠️ Ловушка: возвращать 200 OK с телом {"error": "..."} — антипаттерн (так делают плохие SOAP/legacy API). Клиенты и мониторинг ориентируются на код статуса; ошибка должна иметь 4xx/5xx, иначе сбои «прячутся» под успешным кодом.

07

7. Ключевые коды статусов и когда их использовать

Короткий ответ: Нужно знать минимум 200/201/204, 301/302/304, 400/401/403/404/409/422/429, 500/502/503 и их семантику.

Подробно:

Код Когда
200 OK Успешный GET/PUT/PATCH с телом
201 Created Ресурс создан (обычно после POST), желателен Location
204 No Content Успех без тела (DELETE, PUT без возврата)
301 Moved Permanently Ресурс навсегда по новому URL (кэшируется, влияет на SEO)
302 Found Временное перенаправление
304 Not Modified Кэш клиента актуален (ответ на условный GET)
400 Bad Request Некорректный синтаксис/невалидный JSON, битый запрос
401 Unauthorized Не аутентифицирован (нет/невалидны креды)
403 Forbidden Аутентифицирован, но нет прав
404 Not Found Ресурс не найден
409 Conflict Конфликт состояния (дубликат, конкурентное изменение)
422 Unprocessable Entity Синтаксис ок, но семантически невалидно (валидация бизнес-правил)
429 Too Many Requests Превышен rate limit
500 Internal Server Error Необработанное исключение на сервере
502 Bad Gateway Апстрим вернул некорректный ответ (проблема прокси/сервиса)
503 Service Unavailable Сервис временно недоступен (перегрузка/maintenance), желателен Retry-After

⚠️ Ловушка: 502 vs 503 vs 504. 502 — прокси получил битый ответ от апстрима; 503 — сам сервис недоступен/перегружен; 504 — апстрим не ответил вовремя (таймаут). Путают их постоянно при отладке балансировщиков и API-gateway.

08

8. 401 vs 403, 400 vs 422, 201 vs 204

Короткий ответ: 401 — «кто ты?» (нет аутентификации), 403 — «я знаю кто ты, но нельзя» (нет авторизации). 400 — невалидный синтаксис, 422 — валидный синтаксис, но нарушены бизнес-правила. 201 — создан ресурс (с телом/Location), 204 — успех без тела.

Подробно:

401 vs 403:

  • 401 Unauthorized (на деле «не аутентифицирован») — токен отсутствует, истёк или неверен. Сервер обязан вернуть заголовок WWW-Authenticate.
  • 403 Forbidden — личность установлена, но прав на действие нет (обычный юзер пытается удалить чужой ресурс). Повтор с тем же токеном не поможет.
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="api"

400 vs 422:

  • 400 Bad Request — сервер не смог распарсить запрос: битый JSON, отсутствует обязательный заголовок, неверный тип.
  • 422 Unprocessable Entity — JSON валиден и распарсен, но данные не проходят бизнес-валидацию: email уже занят, дата в прошлом, отрицательная сумма.

201 vs 204:

  • 201 Created — создан новый ресурс; в теле обычно само представление, в заголовке Location — его URI.
  • 204 No Content — операция успешна, но возвращать нечего (типично для DELETE, иногда для PUT/PATCH).

⚠️ Ловушка: многие фреймворки по умолчанию шлют 400 на ошибки валидации. Чёткое разделение 400/422 не обязательно (422 не из core HTTP, а из WebDAV), но полезно: позволяет клиенту различить «я неправильно сформировал запрос» от «данные не прошли проверку». Главное — быть консистентным во всём API.

09

9. HTTP-заголовки: основные и кастомные

Короткий ответ: Заголовки — метаданные запроса/ответа. Ключевые: Content-Type, Accept, Authorization, Cache-Control, ETag, Cookie, User-Agent. Кастомные раньше писали с префиксом X-, теперь рекомендуется без него.

Подробно:

Заголовок Направление Назначение
Content-Type оба формат тела: application/json, text/html, multipart/form-data
Accept запрос какие форматы клиент готов принять (content negotiation)
Authorization запрос креды: Bearer <token>, Basic <base64>
Cache-Control оба политика кэширования: no-cache, max-age=3600, private
ETag ответ версия-«отпечаток» ресурса для условных запросов
Cookie / Set-Cookie запрос / ответ передача и установка cookie
User-Agent запрос информация о клиенте (браузер/SDK)
Location ответ URI созданного ресурса или цель редиректа
Content-Length оба размер тела в байтах
Accept-Encoding / Content-Encoding запрос / ответ сжатие (gzip, br)

Кастомные заголовки (например X-Request-Id, Idempotency-Key): RFC 6648 рекомендует не использовать префикс X- для новых заголовков, но на практике он жив (X-Forwarded-For, X-Real-IP).

⚠️ Ловушка: имена заголовков регистронезависимы (Content-Type == content-type), но значения — нет. В HTTP/2 и HTTP/3 имена передаются в нижнем регистре. Не храните секреты в логируемых заголовках без маскирования (Authorization часто попадает в логи прокси).

10

10. HTTP/1.0 vs 1.1 vs 2 vs 3

Короткий ответ: 1.0 — соединение на каждый запрос; 1.1 — keep-alive и pipelining; 2 — бинарный протокол с мультиплексированием по одному TCP; 3 — поверх QUIC/UDP без head-of-line blocking на транспорте.

Подробно:

HTTP/1.0 — на каждый запрос новое TCP-соединение (дорого: handshake каждый раз).

HTTP/1.1:

  • Connection: keep-alive по умолчанию — переиспользование соединения.
  • Pipelining (несколько запросов без ожидания ответов) — но почти не используется из-за head-of-line blocking: медленный первый ответ блокирует остальные.
  • Chunked transfer encoding, виртуальные хосты (обязательный Host).
  • Браузеры открывают ~6 параллельных соединений на домен (отсюда «domain sharding»).

HTTP/2:

  • Бинарный формат вместо текстового.
  • Мультиплексирование: много параллельных стримов в одном TCP-соединении, без HOL-блокировки на уровне HTTP.
  • Сжатие заголовков (HPACK).
  • Server Push (сервер шлёт ресурсы заранее) — на практике редко используется и удаляется из браузеров.
  • Остаётся HOL-блокировка на уровне TCP: потеря одного пакета тормозит все стримы.

HTTP/3:

  • Поверх QUIC (транспорт на базе UDP).
  • Решает TCP HOL-блокировку: стримы независимы на транспортном уровне.
  • TLS 1.3 встроен, 0-RTT/1-RTT handshake — быстрее установка соединения.
  • Лучше при потерях пакетов и смене сети (мобильные клиенты, connection migration).

⚠️ Ловушка: HTTP/2 не убирает HOL-блокировку полностью — она остаётся на уровне TCP. Именно поэтому появился HTTP/3 на QUIC/UDP. И Server Push в HTTP/2 — это не «панацея»; Chrome выпилил его поддержку.

11

11. HTTPS/TLS: зачем и как кратко устроен handshake

Короткий ответ: HTTPS — это HTTP поверх TLS. TLS даёт шифрование (конфиденциальность), целостность и аутентификацию сервера через сертификат. Handshake согласует ключи и проверяет сертификат.

Подробно:

Зачем: без TLS трафик идёт открытым текстом — провайдер/Wi-Fi/прокси могут читать и подменять (MITM). TLS обеспечивает три гарантии: шифрование, целостность (MAC/AEAD), аутентификацию сервера.

Упрощённый TLS 1.2 handshake:

  1. ClientHello — клиент шлёт версии TLS, набор cipher suites, random.
  2. ServerHello — сервер выбирает cipher, шлёт сертификат (цепочку до доверенного CA).
  3. Клиент проверяет сертификат (подпись CA, срок, домен в CN/SAN).
  4. Обмен ключами (ECDHE) → общий симметричный сеансовый ключ.
  5. Дальше данные шифруются симметрично (быстро).

TLS 1.3 сократил handshake до 1-RTT (и 0-RTT для повторных подключений), убрал устаревшие шифры.

⚠️ Ловушка: TLS аутентифицирует сервер (через сертификат), но по умолчанию не клиента. Для двусторонней аутентификации нужен mTLS (клиентские сертификаты). Также: «зелёный замочек» означает лишь, что соединение шифровано и сертификат валиден, но не что сайт безопасен — фишинг тоже бывает по HTTPS. (Детали сертификатов/CA — в файле по сетям.)

12

12. Почему HTTP stateless и как держат состояние?

Короткий ответ: Каждый HTTP-запрос самодостаточен — сервер не обязан помнить предыдущие. Это упрощает масштабирование (любой запрос на любой инстанс). Состояние «прикрепляют» через cookie+сессию или токены.

Подробно:

Stateless значит: вся информация для обработки запроса — в самом запросе. Сервер не хранит контекст между запросами. Плюсы: горизонтальное масштабирование, отказоустойчивость, простое кэширование, любой инстанс за балансировщиком обработает запрос.

Но приложениям нужно состояние (кто залогинен, корзина). Способы:

  • Session-based (server-side state): сервер хранит сессию, клиенту отдаёт Set-Cookie: session_id=.... На каждом запросе сервер по id достаёт данные из хранилища (Redis/БД). Минус — требует общего хранилища или sticky sessions.
  • Token-based (stateless auth): JWT с подписью содержит claims (user id, роли). Сервер проверяет подпись, ничего не хранит. Плюс — масштабируемо; минус — сложно отозвать до истечения.
  • Cookie — общий механизм транспорта (хранит и session id, и иногда сам токен).
HTTP/1.1 200 OK
Set-Cookie: session_id=abc123; HttpOnly; Secure; SameSite=Lax

⚠️ Ловушка: хранить сессии в локальной памяти инстанса ломает stateless-преимущество — при балансировке на другой инстанс пользователь «разлогинится». Нужно либо общее хранилище (Redis), либо sticky sessions (что снижает отказоустойчивость), либо stateless-токены.

13

13. Что такое REST и его принципы

Короткий ответ: REST (Representational State Transfer) — архитектурный стиль для распределённых систем (диссертация Роя Филдинга). Основан на ресурсах, единообразном интерфейсе и stateless-взаимодействии поверх HTTP.

Подробно:

Шесть ограничений REST:

  1. Client-Server — разделение ответственности UI и хранения данных.
  2. Stateless — сервер не хранит клиентский контекст между запросами.
  3. Cacheable — ответы должны помечаться как кэшируемые/нет.
  4. Uniform Interface — единообразие: идентификация ресурсов через URI, манипуляция через представления, самоописательные сообщения, HATEOAS.
  5. Layered System — клиент не знает, общается ли он напрямую с сервером или через прокси/балансировщик.
  6. Code on Demand (опционально) — сервер может прислать исполняемый код (например JS).

Ключевая идея: ресурсы (существительные) идентифицируются URI, а HTTP-методы (глаголы) задают операции над ними.

⚠️ Ловушка: большинство «REST API» на практике — это REST уровня 2 по Ричардсону (ресурсы + методы + коды), без HATEOAS. Строго говоря, без HATEOAS API не «по-настоящему RESTful» (по Филдингу), но индустрия называет такие API REST. На собесе уточните этот нюанс.

14

14. Уровни зрелости Ричардсона

Короткий ответ: Модель Ричардсона описывает 4 уровня «RESTfulness»: 0 — RPC поверх HTTP, 1 — ресурсы, 2 — HTTP-глаголы и коды, 3 — HATEOAS.

Подробно:

  • Уровень 0 (The Swamp of POX) — один endpoint, всё через POST, HTTP как транспорт для RPC. Пример: POST /api с телом, описывающим метод.
  • Уровень 1 — Ресурсы. Появляются отдельные URI на сущности: /users/1, /orders/5. Но методы ещё не используются по семантике (всё через POST/GET).
  • Уровень 2 — HTTP-глаголы и коды. Правильное использование GET/POST/PUT/DELETE и кодов статусов (200/201/404...). Большинство реальных API здесь.
  • Уровень 3 — HATEOAS. Ответы содержат гиперссылки на доступные действия; клиент «навигирует» по API, не зашивая URI.
{
  "id": 5,
  "status": "pending",
  "_links": {
    "self": { "href": "/orders/5" },
    "cancel": { "href": "/orders/5/cancel", "method": "POST" }
  }
}

⚠️ Ловушка: уровни — это эвристика, а не строгая лестница. Уровень 3 (HATEOAS) на практике редок: усложняет клиент и сервер, выгода спорна для machine-to-machine API с фиксированным контрактом. Не путайте «RESTful» в маркетинге с реальным уровнем 3.

15

15. Что значит RESTful: дизайн URL ресурсов

Короткий ответ: URL должны идентифицировать ресурсы (существительные во множественном числе), а не действия. Глаголы выражаются HTTP-методами. Вложенность отражает иерархию ресурсов.

Подробно:

Хорошо:

GET    /users              # список пользователей
POST   /users              # создать
GET    /users/42           # один пользователь
PUT    /users/42           # заменить
PATCH  /users/42           # частично обновить
DELETE /users/42           # удалить
GET    /users/42/orders    # заказы пользователя 42 (вложенность)
GET    /users/42/orders/7  # конкретный заказ

Правила:

  • Существительные во множественном числе: /users, не /user и не /getUser.
  • Без глаголов в URI: не /createUser, не /users/42/delete.
  • Вложенность для отношений «принадлежит»: /users/42/orders. Но не углубляйтесь дальше 2 уровней — лучше /orders/7, чем /users/42/orders/7/items/3/....
  • kebab-case или lowercase: /order-items, не /orderItems.
  • Действия, не ложащиеся на CRUD, оформляют как «контроллер-ресурс»: POST /orders/7/cancel, POST /payments/9/refund — допустимый компромисс.

⚠️ Ловушка: не пихать действия в query-параметры (GET /users?action=delete&id=42) — это RPC уровня 0 и нарушение safe-семантики GET. И не плодить глубокую вложенность: глубокие URI хрупкие и неудобны. Фильтры — в query (?status=active), не в path.

16

16. HATEOAS

Короткий ответ: HATEOAS (Hypermedia As The Engine Of Application State) — ответы содержат гиперссылки на возможные следующие действия, чтобы клиент не хардкодил URI и переходы.

Подробно:

Идея: клиент знает только entry point, а дальше «следует ссылкам», как человек по сайту. Сервер в каждом ответе сообщает, что можно сделать с ресурсом в его текущем состоянии.

{
  "order_id": 7,
  "status": "paid",
  "total": 5000,
  "_links": {
    "self":    { "href": "/orders/7" },
    "invoice": { "href": "/orders/7/invoice" },
    "refund":  { "href": "/orders/7/refund", "method": "POST" }
  }
}

Если заказ ещё не оплачен — ссылки refund не будет, зато появится pay. Клиент реагирует на наличие ссылки, а не зашивает бизнес-логику.

Форматы: HAL (_links), JSON:API (links), Siren.

⚠️ Ловушка: HATEOAS звучит красиво, но в реальности почти не внедряют: клиенты всё равно знают структуру API, а накладные расходы (генерация ссылок, разбор гипермедиа) высоки. На собесе важно знать, что это и почему уровень 3 — редкость, а не считать его обязательным.

17

17. Версионирование API: URL vs header

Короткий ответ: Версию API задают через URL (/v1/users), заголовок (Accept: application/vnd.api+json; version=1) или query-параметр. URL-версионирование самое популярное и наглядное.

Подробно:

1. В пути URL (самый распространённый):

GET /api/v1/users

Плюсы: видно сразу, легко роутить и кэшировать, удобно тестировать в браузере. Минусы: «нарушает» идею, что URI идентифицирует один ресурс.

2. В заголовке (media type versioning):

GET /api/users
Accept: application/vnd.example.v2+json

Плюсы: URI чистый и стабильный. Минусы: менее наглядно, сложнее тестировать вручную, легко забыть.

3. Кастомный заголовок:

GET /api/users
X-API-Version: 2

4. Query-параметр: GET /api/users?version=2 — просто, но засоряет query.

⚠️ Ловушка: версионируйте только при breaking changes. Добавление нового опционального поля или нового endpoint — обратно совместимо и не требует новой версии. Не плодите v2, v3 на каждое изменение — поддержка старых версий дорогая. Семантика: ломающие изменения = новая major-версия.

18

18. Пагинация: offset vs cursor

Короткий ответ: Offset-пагинация (?limit=20&offset=40) проста, но медленна на больших offset и неустойчива к вставкам. Cursor-пагинация (?limit=20&cursor=...) стабильна и быстра, но не даёт прыгать на произвольную страницу.

Подробно:

Offset / page-based:

GET /users?limit=20&offset=40        # или ?page=3&per_page=20
  • Просто, поддерживает «перейти на страницу N».
  • Минусы: OFFSET 100000 в SQL медленный (БД сканирует и отбрасывает строки). При вставке/удалении между запросами страницы «съезжают» — можно увидеть дубль или пропустить запись.

Cursor / keyset:

GET /users?limit=20&cursor=eyJpZCI6MTIzfQ==
{
  "data": [ ... ],
  "next_cursor": "eyJpZCI6MTQzfQ==",
  "has_more": true
}
  • Cursor кодирует позицию (например id последнего элемента). SQL: WHERE id > :last_id ORDER BY id LIMIT 20 — использует индекс, быстро на любой глубине.
  • Стабильна при вставках. Минусы: нельзя прыгнуть на произвольную страницу, нужна стабильная сортировка по уникальному полю.

⚠️ Ловушка: offset-пагинация без стабильной сортировки (ORDER BY created_at при равных timestamp) выдаёт недетерминированный порядок и дубли/пропуски на границах страниц. Сортируйте по уникальному ключу (или составному с id). Для бесконечной ленты (feed) cursor почти всегда правильный выбор.

19

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

Короткий ответ: Фильтры, сортировку и поиск передают через query-параметры коллекции, не плодя endpoint'ы.

Подробно:

GET /products?category=books&price_min=100&price_max=500&sort=-price,name&q=rest&fields=id,title,price
  • Фильтрация: ?status=active&category=books. Для диапазонов — price_min/price_max или синтаксис price[gte]=100.
  • Сортировка: ?sort=-price,name (минус = по убыванию). Несколько полей через запятую.
  • Поиск: полнотекстовый — отдельный параметр ?q=....
  • Sparse fieldsets: ?fields=id,title — вернуть только нужные поля (борьба с over-fetching).

⚠️ Ловушка: принимать произвольные поля фильтра/сортировки напрямую в SQL — риск SQL-инъекции и нагрузки на неиндексированные поля. Валидируйте имена полей по белому списку и ограничивайте, по чему можно сортировать/фильтровать (только индексированные колонки).

20

20. Единый формат обработки ошибок

Короткий ответ: Все ошибки API должны иметь единый машиночитаемый формат тела с кодом, сообщением и деталями, плюс корректный HTTP-статус. Стандарт — RFC 7807/9457 (Problem Details).

Подробно:

Формат Problem Details (Content-Type: application/problem+json):

HTTP/1.1 422 Unprocessable Entity
Content-Type: application/problem+json

{
  "type": "https://api.example.com/errors/validation",
  "title": "Validation failed",
  "status": 422,
  "detail": "Поле email уже используется",
  "instance": "/users",
  "errors": [
    { "field": "email", "code": "duplicate", "message": "email уже занят" }
  ]
}

Принципы:

  • Стабильный машиночитаемый code/type (клиент ветвится по нему, не по тексту).
  • Человекочитаемое message/detail.
  • Детали по полям для валидации.
  • Не светить стектрейсы и внутренние детали в проде.
  • trace_id/request_id для корреляции с логами.

⚠️ Ловушка: возвращать ошибки в разном формате на разных endpoint'ах (где-то строка, где-то объект, где-то 200 с success:false). Клиент не сможет обрабатывать ошибки единообразно. Зафиксируйте формат на уровне middleware/exception handler.

21

21. Rate limiting и 429

Короткий ответ: Rate limiting ограничивает число запросов за период; при превышении возвращается 429 Too Many Requests с заголовком Retry-After и/или RateLimit-*.

Подробно:

HTTP/1.1 429 Too Many Requests
Retry-After: 30
RateLimit-Limit: 100
RateLimit-Remaining: 0
RateLimit-Reset: 30

Алгоритмы: Token Bucket (плавные всплески), Leaky Bucket, Fixed Window, Sliding Window Log/Counter. Token bucket и sliding window — самые распространённые.

Лимитируют по ключу: API-ключ, user id, IP. Хранят счётчики обычно в Redis (атомарные инкременты с TTL).

Зачем: защита от DDoS/abuse, справедливое распределение ресурсов, защита бэкенда от перегрузки, тарификация (free vs paid tier).

⚠️ Ловушка: rate limit по IP ломается за NAT/прокси (много юзеров за одним IP) и обходится сменой IP. Лучше лимитировать по аутентифицированному субъекту. Также важен Retry-After, чтобы клиент знал, когда повторить, и реализовывал exponential backoff, а не бил в стену.

22

22. Идемпотентность, повторы и at-least-once

Короткий ответ: В распределённых системах доставка часто at-least-once (сообщение может прийти не раз). Чтобы повторы не дублировали эффект, обработчики делают идемпотентными (dedup по ключу/ID).

Подробно:

Гарантии доставки:

  • at-most-once — не более раза, но возможна потеря.
  • at-least-once — не менее раза, возможны дубли. Практичный дефолт (ретраи при таймаутах, переотправка очередей).
  • exactly-once — идеал, недостижим строго; эмулируется через at-least-once + идемпотентность/дедупликацию.

Стратегия: at-least-once на транспорте + идемпотентный приёмник = эффект «ровно один раз». Дедуплицируют по message_id/Idempotency-Key (таблица обработанных ID, уникальный индекс).

Ретраи на клиенте: повторять можно идемпотентные методы (GET, PUT, DELETE), для POST — только с idempotency key. Использовать exponential backoff + jitter, уважать Retry-After.

⚠️ Ловушка: ретраить POST без idempotency key опасно — таймаут не значит «не выполнилось». Запрос мог дойти и обработаться, а ответ потеряться; повтор создаст дубликат (двойное списание). Всегда различайте «не дошёл» и «дошёл, но ответ потерян».

23

23. REST vs RPC vs GraphQL vs gRPC

Короткий ответ: REST — ресурсы поверх HTTP-глаголов; RPC — вызов удалённых процедур (действия); GraphQL — один endpoint с гибким языком запросов; gRPC — бинарный RPC на protobuf поверх HTTP/2.

Подробно:

Критерий REST RPC (JSON-RPC) GraphQL gRPC
Модель ресурсы/существительные процедуры/действия граф данных процедуры/действия
Транспорт HTTP HTTP HTTP (обычно POST) HTTP/2
Формат JSON JSON JSON protobuf (бинарь)
Endpoint'ы много URI один (метод в теле) один (/graphql) сервисы/методы
Over/under-fetch возможен возможен решён (клиент задаёт поля) фикс. контракт
Схема/типы OpenAPI (опц.) слабо строгая (SDL) строгая (.proto)
Стриминг SSE/WS отдельно нет subscriptions встроенный
Кэш по HTTP отличный слабый слабый (POST) нет
Браузер нативно да да нужен grpc-web

Когда что:

  • REST — публичные API, CRUD, нужно HTTP-кэширование и простота.
  • RPC — внутренние сервисы с операциями, не ложащимися на CRUD.
  • GraphQL — клиенты с разными потребностями в данных (мобайл + веб), агрегация многих источников, борьба с over/under-fetching.
  • gRPC — высоконагруженное межсервисное общение (microservices), нужен стриминг и низкая латентность.

⚠️ Ловушка: GraphQL не «лучше REST» по умолчанию. Он переносит сложность на сервер: HTTP-кэширование почти не работает (всё POST на один URL), легко словить N+1-запросы и ресурсоёмкие вложенные запросы (нужны DataLoader, ограничение глубины/сложности). Для простого CRUD REST проще и эффективнее.

24

24. Over-fetching и under-fetching: как GraphQL решает

Короткий ответ: Over-fetching — сервер возвращает больше данных, чем нужно клиенту. Under-fetching — данных не хватает, нужно несколько запросов. GraphQL позволяет клиенту запросить ровно нужные поля за один запрос.

Подробно:

В REST: GET /users/42 вернёт весь объект пользователя, даже если нужно только имя — over-fetching. А чтобы показать пользователя с его заказами и адресами, нужно GET /users/42, GET /users/42/orders, GET /users/42/addressesunder-fetching (N+1 round-trips).

GraphQL — клиент описывает, что хочет, и получает это за один запрос:

query {
  user(id: 42) {
    name
    orders(last: 3) { id total }
  }
}
{ "data": { "user": { "name": "Анна", "orders": [ ... ] } } }

Частичные решения в REST: sparse fieldsets (?fields=name), embed/expand (?include=orders), композитные endpoint'ы (BFF — Backend for Frontend).

⚠️ Ловушка: GraphQL лечит over/under-fetching на клиенте, но на сервере возникает N+1 проблема (резолвер каждого поля бьёт в БД). Решается DataLoader (батчинг + кэш в рамках запроса). Без этого GraphQL может быть медленнее REST.

25

25. gRPC: protobuf, HTTP/2, стриминг

Короткий ответ: gRPC — RPC-фреймворк от Google: контракт в .proto, сериализация в бинарный protobuf, транспорт HTTP/2, поддержка четырёх видов стриминга. Быстрый и типобезопасный, идеален для межсервисного общения.

Подробно:

Контракт описывается в .proto, из него генерируется клиент и сервер на многих языках:

service UserService {
  rpc GetUser (GetUserRequest) returns (User);
  rpc ListUsers (ListRequest) returns (stream User);   // server streaming
}
message GetUserRequest { int32 id = 1; }
message User { int32 id = 1; string name = 2; }

Виды вызовов:

  • Unary — запрос → ответ (как обычный RPC).
  • Server streaming — один запрос → поток ответов.
  • Client streaming — поток запросов → один ответ.
  • Bidirectional streaming — двунаправленный поток поверх одного HTTP/2-соединения.

Преимущества: компактный бинарный формат (меньше REST/JSON), мультиплексирование HTTP/2, строгая схема и кодогенерация, низкая латентность. Минусы: бинарь не читается человеком, в браузере напрямую не работает (нужен grpc-web + прокси), слабее HTTP-кэширование.

⚠️ Ловушка: gRPC требует HTTP/2 end-to-end; не каждый прокси/балансировщик/legacy-инфраструктура это поддерживает. И отлаживать бинарный protobuf руками (как curl с JSON) нельзя — нужен grpcurl. Для публичных браузерных API gRPC обычно неудобен.

26

26. WebSockets vs polling vs SSE

Короткий ответ: Polling — клиент периодически опрашивает сервер; long polling — держит запрос открытым до события; SSE — однонаправленный поток событий сервер→клиент; WebSocket — полнодуплексный двусторонний канал.

Подробно:

Технология Направление Транспорт Когда
Short polling клиент тянет периодически обычный HTTP простота, нечастые обновления
Long polling сервер отвечает при событии HTTP (держит запрос) realtime без WS, fallback
SSE сервер → клиент (one-way) HTTP, text/event-stream ленты, нотификации, прогресс
WebSocket дуплекс (оба направления) апгрейд HTTP → ws чат, игры, коллаборация

Short pollingsetInterval(fetch, 5000). Просто, но лишний трафик и задержка.

Long polling — запрос висит, сервер отвечает при появлении данных, клиент сразу переоткрывает. Меньше пустых ответов, но держит соединения.

SSE — сервер шлёт события в одном долгом HTTP-ответе:

GET /events HTTP/1.1
Accept: text/event-stream
data: {"type":"new_message","id":5}

data: {"type":"typing"}

Однонаправленно, авто-reconnect, поверх обычного HTTP, работает с HTTP/2. Не подходит для отправки от клиента.

WebSocket — после рукопожатия (Upgrade: websocket, ответ 101 Switching Protocols) — постоянный двунаправленный канал, низкие накладные расходы на сообщение.

⚠️ Ловушка: WebSocket — не всегда правильный выбор. Если данные текут только сервер→клиент (биржевые котировки, уведомления), SSE проще: работает поверх HTTP, авто-reconnect из коробки, проще проксируется и масштабируется. WebSocket сложнее в балансировке (stateful соединения) и не кэшируется.

27

27. CORS и preflight

Короткий ответ: CORS (Cross-Origin Resource Sharing) — механизм браузера, разрешающий или запрещающий запросы с одного origin к другому. Для «непростых» запросов браузер сначала шлёт preflight OPTIONS.

Подробно:

Из-за Same-Origin Policy браузер по умолчанию блокирует JS-запросы на другой origin (схема+хост+порт). CORS позволяет серверу явно разрешить кросс-доменные запросы заголовками.

Простые запросы (GET/POST/HEAD с «безопасными» заголовками) идут сразу, сервер отвечает:

Access-Control-Allow-Origin: https://app.example.com

Preflight — для «непростых» запросов (методы PUT/DELETE/PATCH, кастомные заголовки, Content-Type: application/json) браузер сначала шлёт OPTIONS:

OPTIONS /api/users HTTP/1.1
Origin: https://app.example.com
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: authorization
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: authorization, content-type
Access-Control-Max-Age: 86400

Если ответ разрешает — браузер шлёт настоящий запрос.

⚠️ Ловушка: CORS — защита браузера, а не сервера. curl/Postman/мобильные клиенты игнорируют CORS. Это не замена аутентификации/авторизации. И при Access-Control-Allow-Credentials: true нельзя использовать Allow-Origin: * — нужен конкретный origin (детали в файле по auth).

28

28. Content negotiation

Короткий ответ: Content negotiation — механизм, при котором клиент через заголовки Accept* сообщает желаемый формат/язык/кодировку, а сервер выбирает наиболее подходящее представление ресурса.

Подробно:

Клиент указывает предпочтения с весами (q-factor):

GET /report HTTP/1.1
Accept: application/json;q=0.9, application/xml;q=0.5
Accept-Language: ru-RU, en;q=0.7
Accept-Encoding: br, gzip

Сервер выбирает и отвечает:

HTTP/1.1 200 OK
Content-Type: application/json
Content-Language: ru-RU
Content-Encoding: br
Vary: Accept, Accept-Language, Accept-Encoding

Заголовок Vary сообщает кэшам/CDN, что ответ зависит от этих заголовков (нельзя отдать XML-кэш тому, кто просит JSON).

⚠️ Ловушка: забыть Vary при content negotiation — CDN/прокси закэширует один вариант (например gzip-версию) и отдаст её клиенту, не поддерживающему его, либо отдаст JSON тому, кто просил XML. Vary обязателен для корректного кэширования согласованного контента.

29

29. Cookies: атрибуты и виды

Короткий ответ: Cookie — пара ключ-значение, которую сервер устанавливает через Set-Cookie, а браузер возвращает в Cookie. Атрибуты управляют сроком жизни, областью и безопасностью.

Подробно:

Set-Cookie: session_id=abc123; HttpOnly; Secure; SameSite=Lax; Domain=example.com; Path=/; Max-Age=3600
Атрибут Назначение
HttpOnly недоступна из JS (document.cookie) — защита от XSS-кражи
Secure отправляется только по HTTPS
SameSite Strict/Lax/None — контроль отправки в кросс-сайт запросах (защита от CSRF)
Domain для каких доменов действует (включая поддомены)
Path для каких путей действует
Max-Age / Expires срок жизни

Виды:

  • Сессионные (session cookies) — без Max-Age/Expires, удаляются при закрытии браузера.
  • Персистентные (persistent) — с явным сроком, переживают перезапуск браузера.

⚠️ Ловушка: SameSite=None обязан сопровождаться Secure, иначе браузер отклонит cookie. Современные браузеры по умолчанию ставят SameSite=Lax. Для аутентификационных cookie всегда HttpOnly + Secure — иначе XSS украдёт сессию, а сниффер перехватит по HTTP.

30

30. HTTP-кэширование: Cache-Control, ETag, 304, CDN

Короткий ответ: HTTP-кэширование управляется заголовками Cache-Control (как долго и где кэшировать) и валидаторами ETag/Last-Modified для условных запросов, возвращающих 304 Not Modified без тела.

Подробно:

Freshness (Cache-Control):

Cache-Control: public, max-age=3600
Cache-Control: private, no-cache
Cache-Control: no-store
  • max-age=N — сколько секунд ответ «свежий».
  • public — можно кэшировать в общих кэшах (CDN); private — только в браузере.
  • no-cache — кэшировать можно, но перед использованием ревалидировать.
  • no-store — не кэшировать вообще (чувствительные данные).

Validation (условные запросы): Сервер возвращает ETag (хэш/версия) или Last-Modified. При следующем запросе клиент шлёт:

GET /avatar.png HTTP/1.1
If-None-Match: "v3-abc"

Если ресурс не менялся:

HTTP/1.1 304 Not Modified
ETag: "v3-abc"

304 без тела — экономия трафика; браузер берёт ресурс из кэша.

CDN — географически распределённые кэширующие узлы. Они уважают Cache-Control/ETag/Vary и отдают статику/кэшируемые ответы ближе к пользователю, разгружая origin.

⚠️ Ловушка: no-cache != no-store. no-cache разрешает хранить ответ, но требует ревалидации перед использованием; no-store запрещает хранить вовсе. Для приватных данных (банк, персданные) нужен именно no-store. Частая ошибка — думать, что no-cache означает «не кэшировать».

31

31. Сжатие: gzip и brotli

Короткий ответ: Сжатие тела ответа уменьшает трафик. Клиент объявляет поддержку в Accept-Encoding, сервер сжимает и ставит Content-Encoding. gzip — универсальный, brotli (br) — эффективнее для текста.

Подробно:

GET /api/data HTTP/1.1
Accept-Encoding: br, gzip, deflate
HTTP/1.1 200 OK
Content-Encoding: br
Vary: Accept-Encoding
  • gzip — повсеместная поддержка, хорошее соотношение скорость/сжатие.
  • brotli (br) — лучше сжимает текст (HTML/CSS/JS/JSON), особенно на максимальных уровнях, но медленнее на сжатии; идеален для статики, которую сжимают заранее.
  • deflate — устаревший, лучше не использовать.

Сжимать стоит текстовые форматы (JSON, HTML, CSS, JS). Уже сжатые (JPEG, PNG, MP4, zip) сжимать бессмысленно — выгоды нет, только CPU.

⚠️ Ловушка: забыть Vary: Accept-Encoding — CDN может отдать gzip-ответ клиенту, не поддерживающему его. Также: сжатие + шифрование чувствительных данных в одном ответе уязвимо к атакам типа BREACH/CRIME (по размеру сжатого ответа можно угадывать секреты) — не сжимайте ответы, смешивающие секреты и пользовательский ввод.

32

32. multipart/form-data и загрузка файлов

Короткий ответ: multipart/form-data — формат тела для отправки бинарных файлов и полей формы вместе. Каждая часть отделена boundary и имеет свои заголовки.

Подробно:

POST /upload HTTP/1.1
Content-Type: multipart/form-data; boundary=----X9Z

------X9Z
Content-Disposition: form-data; name="title"

Мой документ
------X9Z
Content-Disposition: form-data; name="file"; filename="doc.pdf"
Content-Type: application/pdf

<бинарные данные PDF>
------X9Z--
  • boundary разделяет части; каждая часть — со своими Content-Disposition и опц. Content-Type.
  • Подходит для смешанных данных (текстовые поля + файлы).
  • Альтернатива: application/x-www-form-urlencoded (только текст, без файлов).

Для больших файлов:

  • Chunked / resumable upload (tus, S3 multipart upload) — загрузка частями с возможностью докачки.
  • Presigned URLs — клиент грузит напрямую в объектное хранилище (S3), минуя бэкенд.

⚠️ Ловушка: не загружать большие файлы в память сервера целиком — нужен стриминг на диск/в хранилище и лимит размера (413 Payload Too Large). И обязательно валидировать тип/размер на сервере: Content-Type от клиента можно подделать, проверяйте «магические байты» и ограничивайте расширения, чтобы не загрузили исполняемый файл.

33

33. Структура URL: scheme/host/path/query/fragment

Короткий ответ: URL состоит из схемы, хоста (+порт), пути, строки запроса и фрагмента. Path-параметры идентифицируют ресурс, query-параметры — фильтруют/настраивают.

Подробно:

https://api.example.com:443/v1/users/42?fields=name&active=true#section
└─┬─┘   └──────┬───────┘└┬┘└────┬─────┘└────────┬────────────┘└──┬──┘
scheme       host      port    path           query          fragment
  • scheme — протокол (https, http, ws).
  • host — домен или IP; port — опционален (443 для https, 80 для http).
  • path — иерархический путь к ресурсу.
  • query — пары key=value через & после ?.
  • fragment (#...) — якорь, на сервер не отправляется (обрабатывается браузером).

Path vs query params:

  • Path — идентификация конкретного ресурса (обязательная, иерархическая): /users/42.
  • Query — необязательные модификаторы коллекции: фильтр, сортировка, пагинация: ?status=active&limit=20.

⚠️ Ловушка: fragment (#...) никогда не доходит до сервера — нельзя класть туда данные для API. Также не кладите секреты (токены) в query — они попадают в логи серверов, прокси, историю браузера, заголовок Referer. Токены — в заголовок Authorization.

34

34. Webhooks: зачем и как обезопасить

Короткий ответ: Webhook — HTTP-callback: вместо опроса вашего API сторонним сервисом, сервис сам шлёт POST на ваш URL при событии. Это «push» вместо «pull». Безопасность — через подпись тела.

Подробно:

Пример: платёжный провайдер при успешной оплате шлёт вам POST:

POST /webhooks/payments HTTP/1.1
Content-Type: application/json
X-Signature: t=1700000000,v1=5257a869e7...

{"event":"payment.succeeded","payment_id":"p_123","amount":5000}

Безопасность и надёжность:

  • Подпись (HMAC): провайдер подписывает тело секретом, вы пересчитываете HMAC и сравниваете — это подтверждает подлинность и целостность.
  • Защита от replay: проверяйте timestamp в подписи и отвергайте старые запросы.
  • HTTPS обязателен.
  • Идемпотентность: webhook'и доставляются at-least-once — обрабатывайте по event_id с дедупликацией (один и тот же event может прийти дважды).
  • Быстрый 2xx: отвечайте быстро (200), тяжёлую обработку выносите в очередь; иначе провайдер сочтёт доставку неудачной и будет ретраить.

⚠️ Ловушка: доверять телу webhook без проверки подписи — любой может подделать payment.succeeded и получить товар бесплатно. Сравнение подписей делайте через constant-time comparison (защита от timing-атак), а не обычным ==. И не забывайте про дедупликацию — повторная доставка не должна выдавать товар дважды.

35

35. Зачем идемпотентность и где она спасает?

Короткий ответ: Идемпотентность позволяет безопасно повторять запросы в ненадёжной сети, не создавая дубликатов и побочных эффектов. Спасает при таймаутах, ретраях, повторной доставке сообщений.

Подробно:

Сеть ненадёжна: запрос может дойти, а ответ — потеряться. Клиент видит таймаут и не знает, выполнилась операция или нет. Варианты:

  1. Не повторять — рискуем потерять операцию.
  2. Повторять идемпотентную операцию — безопасно, эффект тот же.
  3. Повторять неидемпотентную (POST оплаты) без защиты — дубликат (двойное списание).

Где спасает:

  • Платежи: idempotency key не даёт списать дважды при ретрае.
  • Очереди сообщений: at-least-once доставка → дубли; идемпотентный consumer (dedup по message_id) делает эффект «exactly-once».
  • Распределённые транзакции / Saga: шаги повторяемы при сбоях.
  • Инфраструктура (Terraform, k8s): desired state применяется идемпотентно — повторный apply ничего не ломает.

⚠️ Ловушка: идемпотентность нужно проектировать заранее — она почти не «прикручивается» поверх готовой неидемпотентной системы. Если архитектура изначально допускает at-least-once (а в распределённых системах это норма), обработчики обязаны быть идемпотентными с первого дня.

36

36. Чем REST лучше или хуже GraphQL?

Короткий ответ: REST проще, отлично кэшируется по HTTP и предсказуем; GraphQL гибче для клиента (нет over/under-fetching) и хорош при множестве разнородных клиентов, но переносит сложность на сервер и ломает HTTP-кэширование.

Подробно:

REST лучше когда:

  • Простой CRUD, ресурсная модель ложится естественно.
  • Нужно HTTP-кэширование (CDN, ETag, 304) — работает «из коробки».
  • Публичное API с понятным контрактом, важна простота интеграции.
  • Файлы, стриминг, разные content-types.

GraphQL лучше когда:

  • Много клиентов с разными потребностями в данных (мобайл экономит трафик, веб тянет больше).
  • Агрегация данных из многих сервисов в один запрос (BFF).
  • Часто меняющиеся требования к выборке полей без правки бэкенда.
  • Сильная типизация и интроспекция схемы важны.

Цена GraphQL:

  • HTTP-кэширование почти не работает (всё POST на /graphql).
  • N+1-проблема (нужен DataLoader).
  • Сложно ограничивать дорогие/глубокие запросы (нужны depth/complexity limits, persisted queries).
  • Rate limiting сложнее (один запрос != одна «стоимость»).

⚠️ Ловушка: выбор «REST vs GraphQL» — не про «что современнее», а про контекст. На собесе плохой ответ — «GraphQL лучше, потому что новее». Хороший — «зависит от: числа и разнообразия клиентов, важности кэширования, сложности агрегации». Часто их даже комбинируют: GraphQL для клиентского BFF, REST/gRPC между сервисами.

Источники

Источники и редакционная политика

Материалы RecallDeck сопоставлены с официальной документацией и открытыми публикациями компаний, когда первичный источник доступен. Мы не связаны с упомянутыми работодателями, не публикуем конфиденциальные задания и не продаём места в подборках. Формат найма может меняться — уточняйте его у рекрутера.

От чтения к воспроизведению

Отрепетируйте полный цикл интервью.

RecallDeck возвращает сложные темы по расписанию и помогает удерживать в памяти язык, SQL, архитектуру и поведенческие истории.

Начать подготовку

Продолжить подготовку

Библиотека собеседований RecallDeck

Подробные русские ответы, разборы этапов найма и планы подготовки для российского IT-рынка.

RSS