REST vs GraphQL vs gRPC и чек-лист хорошего API

Кульминация курса: где у REST границы, чем его дополняют GraphQL и gRPC, и единый чек-лист, который держит ваш API в форме.

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

Весь курс мы строили REST: ресурсы-существительные, HTTP-методы, статусы, версии, кэш. Это отличный выбор по умолчанию — особенно для публичных API и веб-бэкендов. Но инженер обязан знать, где у инструмента границы. В этом финальном уроке мы сравним три стиля, разберём, когда выбирать каждый, и соберём всё пройденное в один чек-лист хорошего REST API.

REST: ресурсы, HTTP и кэш

Сильные стороны REST — простота и встроенность в веб. URI описывают ресурсы (/orders/42), методы задают действие (GET, POST, PUT, DELETE), статусы сообщают результат. Главный козырь — HTTP-кэширование: ответы на GET кэшируются браузерами, CDN и прокси «бесплатно», по заголовкам ETag и Cache-Control. Слабость — over-fetching и under-fetching: эндпоинт отдаёт фиксированный объект. Нужно лишь имя пользователя — получаешь весь профиль (over-fetching). Нужны заказ и имена товаров в нём — делаешь несколько запросов (under-fetching, проблема N+1).

GraphQL: гибкая выборка, один эндпоинт

GraphQL переворачивает модель: вместо многих эндпоинтов — один (POST /graphql), а клиент сам описывает в запросе, какие поля и связи ему нужны. Сервер возвращает ровно это — ни больше, ни меньше.

query {
  order(id: 42) {
    total
    items {
      title
      price
    }
  }
}

Одним запросом клиент получил заказ и вложенные товары — без over-fetching и без N+1. Цена гибкости: кэшировать сложнее (все запросы — это POST на один URL, HTTP-кэш не работает из коробки), сервер сложнее (нужен слой резолверов), и легко написать «запрос-бомбу», глубоко вложенный и тяжёлый. GraphQL блестит там, где клиентов много и у каждого свои потребности в данных — например, мобильное и веб-приложение поверх одного API.

gRPC: бинарный protobuf, стриминг, межсервис

gRPC — это про скорость и строгий контракт между сервисами. Вместо JSON-текста — бинарный формат Protocol Buffers (protobuf): данные сериализуются компактно и парсятся в разы быстрее. Контракт описывается в .proto-файле, из которого генерируется типизированный код клиента и сервера.

service OrderService {
  rpc GetOrder (OrderRequest) returns (Order);
  rpc WatchOrders (Empty) returns (stream Order);
}

message OrderRequest {
  int32 id = 1;
}

gRPC работает поверх HTTP/2 и умеет стриминг — потоковую двустороннюю передачу (метод WatchOrders отдаёт поток заказов). Это идеально для внутреннего межсервисного обмена в микросервисной архитектуре, где важны латентность и пропускная способность. Минусы: бинарный формат не читается человеком и не открывается в браузере напрямую, отладка сложнее, для веб-клиентов нужен прокси (gRPC-Web).

Когда что выбирать

КритерийRESTGraphQLgRPC
ФорматJSON (текст)JSON (текст)protobuf (бинарь)
Эндпоинтымного (по ресурсам)одинметоды сервиса
Выборка полейфиксированнаягибкая, под клиентафиксированная
HTTP-кэшотличныйслабыйнет (своя логика)
Стримингограниченночерез subscriptionsвстроенный
Читаемость в браузередаданет
Лучше всего дляпубличные/веб-APIмного клиентов, разные данныемежсервис, низкая латентность

Практическое правило: публичный API или веб-бэкенд — REST; много разнородных клиентов с разными запросами данных — GraphQL; внутренний обмен между сервисами с упором на производительность — gRPC. Стили не взаимоисключающие: крупная система часто использует REST на границе, gRPC внутри и GraphQL как агрегирующий слой.

Как работает под капотом

Разница в трёх осях. Транспорт: REST и GraphQL обычно поверх HTTP/1.1 с текстовым JSON; gRPC требует HTTP/2 ради мультиплексирования и стриминга. Сериализация: JSON — текст, человекочитаемый, но «толстый» и медленный в парсинге; protobuf — бинарь по схеме, где поля кодируются номерами (id = 1), а не именами, поэтому компактнее и быстрее. Маршрутизация: в REST URL+метод однозначно указывают на обработчик; в GraphQL один URL, а разбор делает движок резолверов по дереву запроса; в gRPC вызов — это «процедура» сервиса, как удалённый вызов функции. Понимание этих осей объясняет все компромиссы из таблицы выше.

Частые ошибки выбора

  • GraphQL ради моды. Один клиент и простые данные — REST проще и кэшируется лучше. GraphQL добавляет сложность резолверов без выгоды.
  • gRPC наружу. Публичный API на gRPC мучает внешних разработчиков: не открыть в браузере, нужен прокси. Наружу — REST или GraphQL.
  • REST там, где N+1 неизбежен. Если клиенты вечно делают по 10 запросов на экран — это сигнал к GraphQL или к продуманным составным эндпоинтам.
  • «Один стиль на всё». Зрелые системы смешивают стили по слоям, а не выбирают один религиозно.

Финальный чек-лист хорошего REST API

Итог всего курса. Пройдитесь по нему перед тем, как назвать API готовым:

  1. URI — существительные, не глаголы. /orders, а не /getOrders. Множественное число, иерархия ресурсов.
  2. Правильные методы. GET читает, POST создаёт, PUT/PATCH обновляют, DELETE удаляет. GET и DELETE/PUT — без побочных сюрпризов, идемпотентны где положено.
  3. Корректные статусы. 200/201/204 на успех, 400/401/403/404/409 на ошибки клиента, 500 на ошибки сервера. Никаких «200 с полем error внутри».
  4. Единый формат ошибок. Один JSON-конверт ошибки на весь API: код, сообщение, детали. Клиент парсит его одинаково везде.
  5. Пагинация. Списки не отдаются целиком — limit/offset или cursor, с метаданными о следующей странице.
  6. Версионирование. /v1/ в пути или через заголовок. Ломающие изменения — в новой версии, не в текущей.
  7. Аутентификация и авторизация. Токены (Bearer/JWT), проверка прав на каждый ресурс. Никаких «забыли защитить эндпоинт».
  8. Rate limiting. Лимиты на частоту запросов, заголовки X-RateLimit-*, статус 429 при превышении.
  9. Кэширование. ETag, Cache-Control для GET — экономия трафика и нагрузки.
  10. Документация. Актуальная OpenAPI-спека, отрендеренная в Swagger UI/Redoc, в репозитории и проверяемая.
  11. Тесты. Автотесты эндпоинтов, контрактные тесты, smoke в CI. Зелёный пайплайн — условие релиза.

Итоги

  • REST силён ресурсами, статусами и HTTP-кэшем; слаб на over/under-fetching.
  • GraphQL даёт клиенту гибкую выборку через один эндпоинт ценой сложного кэша и сервера.
  • gRPC — бинарный protobuf, стриминг и низкая латентность для межсервисного обмена, но не для браузера.
  • Выбор: REST — публичные/веб-API, GraphQL — много разных клиентов, gRPC — внутренний межсервис.
  • Хороший REST API проходит чек-лист: существительные-URI, методы, статусы, единые ошибки, пагинация, версии, аутентификация, rate limit, кэш, документация, тесты.
Проверьте себя
1. В чём ключевое преимущество GraphQL над REST для клиентов с разными потребностями в данных?
AБинарный формат и максимальная скорость
BКлиент сам описывает нужные поля и получает ровно их, избегая over- и under-fetching
CЛучшее HTTP-кэширование из коробки
DВстроенный двусторонний стриминг
2. Для какой задачи лучше всего подходит gRPC?
AПубличный API для внешних разработчиков в браузере
BМежсервисный обмен внутри системы с упором на низкую латентность и стриминг
CSEO-страницы сайта
DПростой CRUD с сильной зависимостью от HTTP-кэша
3. Что из перечисленного НЕ соответствует чек-листу хорошего REST API?
AURI-существительные вроде /orders
BЕдиный формат ошибок
CВозврат кода ошибки в теле при статусе 200 OK
DПагинация списков