Logo
Overview

gRPC vs REST vs GraphQL: что выбрать аналитику при проектировании

June 15, 2026
13 min read

gRPC vs REST vs GraphQL: что выбрать аналитику при проектировании

gRPC vs REST — это не холивар из курилки, где побеждает самый громкий разработчик. Это выбор инструмента под задачу. Представьте: вы проектируете интеграцию, и вам нужно описать, как два сервиса будут общаться. Вы пишете ТЗ, согласовываете с командой — а через неделю выясняется, что REST, который вы заложили, не тянет стриминг, а JSON съедает 40% времени на сериализацию. Переделывать контракты через месяц после старта разработки — удовольствие ниже среднего.

В этом посте мы разберём, когда брать REST, когда gRPC, а когда GraphQL — и, что важнее, как аналитику описать выбор в документации так, чтобы архитектор не переделывал его на первой же встрече. Если вы пока плаваете в основных принципах REST API — сначала туда, потом возвращайтесь.

Что скрывается за аббревиатурами

Три протокола — три философии. Они решают одну задачу (передать данные между клиентом и сервером), но подходят к ней с совершенно разных сторон.

REST (Representational State Transfer)

Архитектурный стиль, построенный вокруг ресурсов. Каждый ресурс — это URL, с которым работают через HTTP-методы: GET /orders/42, POST /orders, DELETE /orders/42. Тело ответа — почти всегда JSON. Нет строгого контракта: сервер может добавить поле в ответ, клиент его проигнорирует. И сервер может убрать поле — клиент упадёт (но это уже другая история).

REST — это не протокол, а набор соглашений. Нет единого стандарта, нет компилятора, который проверит контракт. Что хорошо для гибкости — плохо для предсказуемости.

Аналогия: ресторан с меню. Каждое блюдо (ресурс) — отдельная строка. Вы говорите официанту «принеси борщ» (GET), «запиши в счёт компот» (POST), «убери салат» (DELETE). Официант понимает вас, потому что вы оба знаете правила ресторана. Но если шеф-повар сегодня заменил борщ на рассольник, не предупредив официанта — вы получите не то, что заказывали.

gRPC (gRPC Remote Procedure Call)

Высокопроизводительный RPC-фреймворк от Google. Работает поверх HTTP/2, данные сериализуются в бинарный формат Protobuf (Protocol Buffers). Главное отличие от REST: строгий контракт. Вы пишете .proto-файл, описываете сервисы и сообщения, компилятор генерирует клиентский и серверный код на всех поддерживаемых языках. Компилятор не даст клиенту вызвать метод с неправильной сигнатурой.

Protobuf — бинарный формат сериализации. Меньше JSON в 3–10 раз по размеру, быстрее на сериализацию/десериализацию. Жёсткая типизация: int32, string, repeated, oneof — компилятор проверяет всё.

Аналогия: курьерская служба с унифицированными накладными. Вы заполняете бланк строго по полям: отправитель, получатель, вес, габариты. На складе не нужно «догадываться», что написано в графе «особые отметки» — все поля формализованы. Это быстрее, чем писать от руки, но если cargo-форма не предусматривает поле «хрупкое» — вы его не добавите без перевыпуска формы.

GraphQL

Язык запросов к API, разработанный Facebook. Один эндпоинт (обычно /graphql), клиент сам описывает в теле запроса, какие поля и связи ему нужны. Сервер возвращает ровно то, что запросили — ни байтом больше. Это решает проблему over-fetching (REST отдаёт 30 полей, мобилке нужны 3) и under-fetching (чтобы собрать страницу, нужно сделать 5 запросов в разные эндпоинты).

Схема GraphQL — это строго типизированное описание всех доступных данных и операций. Клиент не может запросить поле, которого нет в схеме. Но в отличие от Protobuf, схема не компилируется в код — она живёт на сервере и отдаётся через introspection.

Аналогия: шведский стол. Вы берёте поднос и сами набираете ровно то, что хотите съесть. Хотите только горячее и десерт? Пожалуйста. Хотите пять видов салата и ни одного супа? Без проблем. Но если на раздачу пришло 500 человек одновременно — начинается давка (N+1 проблема GraphQL).

Матрица выбора: диаграмма принятия решений

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

100%
graph TD
  START["Выбор API-протокола для интеграции"] --> Q1{"Кто клиент и какой контекст?"}

  Q1 -->|"Публичное API / веб-приложение"| Q2{"Сложные выборки с фильтрацией?"}
  Q1 -->|"Межсервисное B2B-взаимодействие"| Q4{"Критична ли latency и throughput?"}
  Q1 -->|"Мобильное приложение"| Q5{"Нужна гибкость запросов?"}

  Q2 -->|"Да"| GRAPHQL["GraphQL — единый эндпоинт с query-языком"]
  Q2 -->|"Нет"| REST1["REST API — ресурсы и HTTP-методы"]

  Q4 -->|"Да — сотни RPS"| Q6{"Нужен bidirectional streaming?"}
  Q4 -->|"Нет — десятки RPS"| REST2["REST API — проще не придумаешь"]

  Q6 -->|"Да"| GRPC1["gRPC — Protobuf + HTTP/2"]
  Q6 -->|"Нет"| REST3["REST API с JSON"]

  Q5 -->|"Да — разный набор полей"| GRAPHQL2["GraphQL — клиент сам управляет выборкой"]
  Q5 -->|"Нет — фиксированный контракт"| REST4["REST API"]

  style START fill:#7b68ee,stroke:#5a4db2,color:#fff
  style Q1 fill:#f0a500,stroke:#c88400,color:#fff
  style Q2 fill:#f0a500,stroke:#c88400,color:#fff
  style Q4 fill:#f0a500,stroke:#c88400,color:#fff
  style Q5 fill:#f0a500,stroke:#c88400,color:#fff
  style Q6 fill:#f0a500,stroke:#c88400,color:#fff
  style GRAPHQL fill:#50c878,stroke:#3a9a5c,color:#fff
  style REST1 fill:#4a90d9,stroke:#2c5f8a,color:#fff
  style GRPC1 fill:#50c878,stroke:#3a9a5c,color:#fff
  style REST2 fill:#4a90d9,stroke:#2c5f8a,color:#fff
  style REST3 fill:#4a90d9,stroke:#2c5f8a,color:#fff
  style GRAPHQL2 fill:#50c878,stroke:#3a9a5c,color:#fff
  style REST4 fill:#4a90d9,stroke:#2c5f8a,color:#fff

На диаграмме жёлтые ромбы — вопросы, которые аналитик должен задать (себе и команде) до начала проектирования. Зелёные узлы — сценарии, где GraphQL или gRPC дают ощутимый выигрыш. Синие — выбор в пользу REST. Обратите внимание: REST занимает большую часть финальных ответов. Это не случайность — для 70% интеграций REST действительно оптимален. Но есть 30%, где неправильный выбор бьёт по скорости разработки, latency и масштабируемости. Про эти 30% — дальше.

Таблица сравнения: REST vs gRPC vs GraphQL

Цифры и факты, разложенные по полочкам. Не для холивара — для ТЗ.

КритерийREST (JSON/HTTP)gRPC (Protobuf/HTTP/2)GraphQL
Формат данныхJSON (текстовый)Protobuf (бинарный)JSON (текстовый)
ТранспортHTTP/1.1, HTTP/2HTTP/2 (обязателен)HTTP/1.1, HTTP/2
КонтрактOpenAPI (опционально, на доброй воле).proto-файл (обязателен, компилятор проверяет)Schema (обязательна, introspection)
СовместимостьЛюбой HTTP-клиент, curlГенерируемые клиенты, curl не прокатитЛюбой HTTP-клиент, POST-запросы
Скорость сериализацииСредняя (JSON-парсинг)Высокая (бинарный, до 7x быстрее JSON)Зависит от реализации (обычно JSON)
Размер сообщенияБольшой (текстовые ключи повторяются)Компактный (3–10x меньше JSON)Управляемый (клиент решает, но оверхед ключей остаётся)
StreamingТолько server-sent events (костыль)Нативный: unary, server, client, bidirectionalSubscription (через WebSocket)
Over-fetching / Under-fetchingЧастая проблемаНет проблемы (жёсткий контракт)Решён из коробки
КэшированиеHTTP-кэш (ETag, Cache-Control)Не предусмотрен на уровне протоколаТребует ручного слоя
Версионирование/v1/, /v2/ в URL или заголовкахПоля с номерами в Protobuf, обратная совместимость@deprecated в схеме, эволюция без версий
Браузерная поддержкаНативнаяТребует gRPC-web проксиНативная (POST-запросы)
Кривая входаНизкаяВысокая (Protobuf, генерация кода, HTTP/2)Средняя (схема, резолверы, N+1)

Важно: «меньше» и «быстрее» в таблице — это объективные цифры, но они не означают «лучше». Если ваша интеграция передаёт 50 заказов в час — вам без разницы, Protobuf там или JSON, быстрее на 3 миллисекунды или на 20. Таблица — инструмент для осознанного выбора, а не для доказательства, что «gRPC — это круто».

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

REST — это дефолт. С него стоит начинать, если нет явных причин для другого протокола. Сценарии, где REST оптимален:

  • Публичное API. Партнёры, сторонние разработчики, open API. Вы не можете заставить всех внешних потребителей генерировать клиент из .proto. Они хотят curl, Postman и документацию на Swagger. Подробнее про последний пункт — в разборе API Gateway, который часто становится точкой входа для публичного REST.
  • Веб-приложение с браузерным клиентом. Браузер нативно работает с HTTP и JSON. gRPC требует прокси (grpc-web), GraphQL требует поднятия отдельного сервера или библиотеки. Лишние компоненты — лишние точки отказа.
  • Низкая и средняя нагрузка. До ~500 RPS REST не узкое место. Проблемы начинаются, когда вам нужно гонять сотни тысяч сообщений в секунду между сервисами — но это уже не про REST.
  • Команда без опыта в gRPC/GraphQL. Если никто не работал с Protobuf — внедрение gRPC затянется на недели. Кривая входа реально высокая. За это время можно написать три REST-интеграции и пойти пить кофе.
  • HTTP-кэширование критично. REST работает с CDN, прокси и браузерным кэшем из коробки. В gRPC и GraphQL с этим сложнее — требует дополнительной прослойки.

Простая ментальная модель: если вы проектируете API, которое будут дёргать люди (а не только машины) — REST. Если у вас стандартный CRUD (Create, Read, Update, Delete) — REST. Если вам нужно, чтобы интеграция заработала завтра, а не через месяц — REST.

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

Вот тут начинается интересное. gRPC даёт выигрыш в трёх сценариях — и проигрывает везде, где эти сценарии не критичны.

Межсервисное взаимодействие с высоким RPS

Внутри кластера, где Order Service вызывает Payment Service 50 000 раз в секунду — JSON превращается в бутылочное горлышко. Парсинг строк, повторяющиеся ключи ("orderId":, "customerId":), текстовое представление чисел — всё это на высоких нагрузках съедает CPU и сетевое время.

Protobuf-сообщение с теми же данными на 60–80% меньше. Умножьте на 50 000 запросов в секунду — разница превращается в десятки мегабит в секунду на ровном месте. А если добавить HTTP/2 с мультиплексированием (один TCP-соединение на множество параллельных запросов) — REST на HTTP/1.1 выглядит как велосипед рядом с болидом.

Стриминг данных

REST — это синхронный запрос-ответ. Отправил GET, получил JSON, соединение закрыто. Это нормально, пока вам не нужно:

  • Server streaming: сервис аналитики запрашивает отчёт, а бэкенд отправляет порции данных по мере готовности — клиент начинает обрабатывать первую порцию, не дожидаясь финала.
  • Client streaming: IoT-датчик отправляет показания потоком, сервер агрегирует и раз в минуту сохраняет результат.
  • Bidirectional streaming: голосовой ассистент посылает аудиопоток на сервер, сервер в реальном времени возвращает текст распознавания.

В REST всё это — костыли. Server-sent events для server streaming, чанкованные upload для client streaming, WebSocket для bidirectional. В gRPC все три режима — нативные: одна строчка stream в .proto-файле.

Строгий контракт с кодогенерацией

Когда сервер и клиент пишутся в одном проекте (или хотя бы в одной компании), жёсткий контракт .proto — это подарок. Компилятор генерирует типизированные клиенты и серверные заготовки на Go, Java, Python, C++, Kotlin, Rust и ещё десятке языков. Ошибка в названии поля или типе всплывает на этапе компиляции, а не в проде в три часа ночи.

Аналитику проще всего представить это так: .proto-файл — это контракт, который невозможно нарушить случайно. REST с OpenAPI даёт похожий эффект, но только если команда дисциплинированно поддерживает спеки, генерирует клиентов и не правит JSON руками «по-быстрому» (то есть почти никогда).

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

GraphQL решает одну конкретную боль: клиентам нужно разное. Мобильному приложению нужны 2 поля из сущности «заказ», веб-кабинету — 15 полей, а админке — вообще другая агрегация. В REST вы либо делаете три разных эндпоинта (и плодите сущности), либо отдаёте все 30 полей всем подряд.

Сценарии GraphQL

  • Мобильное приложение с десятком экранов. Каждый экран хочет свой набор данных. GraphQL позволяет фронтендеру собрать ровно то, что нужно для конкретного экрана — без правок на бэке и без «API для страницы 7».
  • Агрегация данных из нескольких источников. GraphQL-сервер может быть надстройкой над REST-сервисами: один запрос от клиента, сервер ходит в три микросервиса, собирает ответ, возвращает клиенту. Правда, внутри вы получаете классическую N+1 проблему и dataloader как обязательный инструмент.
  • Публичное API с непредсказуемыми запросами. Партнёры хотят дёргать ваши данные как угодно, и вы не можете предугадать, какие поля им понадобятся через полгода. GraphQL даёт им гибкость без вашего участия.

Когда GraphQL избыточен

  • У вас один клиент. Если клиент ровно один — вся гибкость GraphQL не нужна. REST с нормальным контрактом решит задачу быстрее.
  • Простые CRUD-операции. GraphQL ради GET /orders/42 — это как брать такси до соседнего подъезда. Можно, но зачем.
  • Нет экспертизы в команде. N+1 проблема, кэширование (не HTTP-кэш!), complexity analysis для защиты от тяжелых запросов, пермишны на уровне полей — GraphQL тащит за собой хвост инфраструктурных задач, которые в REST решаются проще.

Как аналитику описывать контракты для каждого протокола

Хватит теории — давайте к документам, которые вы понесёте команде.

REST: OpenAPI-спек

openapi: 3.0.0
info:
title: Orders API
version: 1.0.0
paths:
/orders:
post:
summary: Создать заказ
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/CreateOrderRequest'
responses:
'201':
description: Заказ создан
content:
application/json:
schema:
$ref: '#/components/schemas/Order'
components:
schemas:
CreateOrderRequest:
type: object
required: [customerId, items]
properties:
customerId:
type: integer
items:
type: array
items:
$ref: '#/components/schemas/OrderItem'

Аналитику достаточно описать сущности и эндпоинты — разработчик допишет технические детали (авторизацию, коды ошибок). Главное — не уйти в «а потом допишем». OpenAPI без кодогенерации — это просто красивый YAML. С кодогенерацией — это половина серверного кода из коробки.

gRPC: Protobuf-контракт

syntax = "proto3";
package orders;
service OrderService {
rpc CreateOrder (CreateOrderRequest) returns (Order);
rpc GetOrder (GetOrderRequest) returns (Order);
rpc StreamOrders (OrderFilter) returns (stream Order);
}
message CreateOrderRequest {
int64 customer_id = 1;
repeated OrderItem items = 2;
string currency = 3; // ISO 4217
}
message OrderItem {
string sku = 1;
int32 quantity = 2;
int64 price = 3; // в минимальных единицах валюты (копейки)
}
message Order {
int64 order_id = 1;
int64 customer_id = 2;
repeated OrderItem items = 3;
int64 total_amount = 4;
string currency = 5;
string status = 6;
}

Номера полей (= 1, = 2) — это не для красоты. Protobuf идентифицирует поля по номерам, а не по именам. Поэтому вы можете переименовать customer_id в buyer_id — обратная совместимость сохранится. Но если поменяете номер поля — предыдущие клиенты сломаются. Это первое, что аналитик должен объяснить команде: номера полей — часть контракта навсегда.

GraphQL: схема

type Query {
order(id: ID!): Order
orders(filter: OrderFilter, first: Int, after: String): OrderConnection
}
type Mutation {
createOrder(input: CreateOrderInput!): CreateOrderPayload
}
type Order {
id: ID!
customer: Customer
items: [OrderItem!]!
totalAmount: Int
currency: String
status: OrderStatus
}
input CreateOrderInput {
customerId: ID!
items: [OrderItemInput!]!
currency: String = "RUB"
}
enum OrderStatus {
CREATED
CONFIRMED
PAID
SHIPPED
CANCELLED
}

Что важно зафиксировать аналитику: схема GraphQL — это не просто список полей. Это контракт, по которому клиент может построить любой валидный запрос. А вы должны гарантировать, что любой валидный запрос отработает. Поле order(id: ID!) — ок. Но если вы добавили orders(filter: ...) без first и без ограничения по complexity — ждите клиента, который запросит orders { items { ... } } на миллион записей и уронит базу (примечательно, что обычно это происходит в пятницу вечером).

Что писать в ТЗ: чеклист для аналитика

При проектировании интеграции зафиксируйте в requirements-документе ответы на эти вопросы — на первой же встрече с архитектором это сэкономит полчаса спора:

  1. Кто потребитель? Браузер / мобильное приложение / другой микросервис / внешний партнёр.
  2. Ожидаемый RPS и размер сообщений. 10 запросов в час или 10 000 в секунду — выбор протокола будет разным.
  3. Нужен ли стриминг? Server, client или bidirectional. Если да — gRPC почти без альтернативы.
  4. Критична ли latency? Если клиент ждёт ответа и каждая миллисекунда на счету (high-frequency trading, real-time bidding) — Protobuf против JSON даёт измеримый выигрыш.
  5. Кто контролирует клиента и сервер? Одна команда — gRPC. Разные команды — REST. Внешние партнёры — только REST (и GraphQL как опция).
  6. Нужна ли обратная совместимость? Если клиенты обновляются раз в полгода (мобильные приложения) — версионирование REST или @deprecated в GraphQL обязательны. Если клиент и сервер деплоятся вместе — .proto с номерами полей решает задачу.
  7. Экспертиза команды. Если никто не знает Protobuf — внедрение gRPC займёт не день и не неделю. Учитывайте это в сроках.

Не надо писать «использовать gRPC» и умывать руки. Опишите, почему именно gRPC, на какие сценарии, с каким контрактом. Иначе через месяц разработчик перепишет на REST «потому что так быстрее» — и будет по-своему прав.

Заключение

gRPC vs REST vs GraphQL — это не вопрос «что лучше». Это вопрос «что лучше для конкретной интеграции». Если у вас публичное API и внешние партнёры — REST, без вариантов. Если внутренний обмен данными с сотнями тысяч RPS — gRPC. Если мобильное приложение с десятком экранов и разные наборы данных — GraphQL.

Большинство проектов, на которых я был, использовали смесь: REST для внешнего API (через шлюз), gRPC для синхронного межсервисного общения, Kafka для асинхронных событий. И это не «сложная архитектура ради архитектуры». Это когда каждый инструмент на своём месте — и никакой холивар в курилке этого не отменяет.

PS. Парадокс в том, что выбор протокола — на 70% аналитическая задача и на 30% техническая. Но в реальности её почти всегда делегируют разработчикам, которые выбирают то, с чем работали последние три года. Если вы как аналитик придёте на встречу с матрицей из этой статьи — поверьте, архитектор скажет спасибо. Или как минимум перестанет закатывать глаза.