Что это
Пять протоколов, между которыми сегодня выбирают для API.
- REST -- HTTP-методы на ресурсах. JSON. Проще всего.
- GraphQL -- один endpoint, клиент описывает нужные поля, сервер возвращает ровно их.
- gRPC -- бинарный (protobuf) + HTTP/2; типизированные контракты, streaming.
- WebSocket -- постоянное bidirectional соединение поверх HTTP upgrade.
- SSE (Server-Sent Events) -- однонаправленный поток от сервера клиенту по обычному HTTP.
Каждый оптимизирован под разный access pattern. "REST vs gRPC" -- часто false dichotomy; в реальном проекте используются несколько.
Когда REST лучше
- Публичный HTTP API. Всё знает REST, все инструменты (curl, Postman, CDN) работают из коробки.
- CRUD-ресурсы.
GET /users/:id,POST /users,DELETE /users/:id-- естественно. - Cache на уровне HTTP. GET с ETag / Cache-Control кешируется CDN без кастомного кода.
- Команда разная. REST знает каждый junior-разработчик.
- Нет жёстких требований на payload size / latency.
Минусы: over-fetching (возвращается больше, чем нужно), under-fetching (нужно несколько запросов), нет стандарта схемы (OpenAPI помогает, но он описание, не код).
Когда GraphQL лучше
- Клиент хочет гибко выбирать поля. Мобильный клиент -- мало полей и данных; admin-панель -- много.
- Один запрос вместо N. Пользователь + его заказы + платежи -- один GraphQL-запрос вместо 3 REST.
- Быстрая эволюция UI. Добавить новое поле в ответ -- без новой версии API.
- Схема -- единственный источник правды. Генерация типов для TS / Flow / Swift.
Минусы:
- Сложнее кеширование (persisted queries помогают).
- N+1 без DataLoader.
- Overhead на GraphQL-движок.
- Сложнее rate limit и авторизация per-field.
- Деплой схемы -- важный процесс (breaking changes).
Когда gRPC лучше
- Сервис-to-сервис (internal). Меньший payload (protobuf ~30% от JSON), HTTP/2 multiplexing, типы из коробки.
- Streaming. Server streaming, client streaming, bidirectional streaming.
- Полиглот-стек. .proto -> клиенты на Go/Java/Python/TS/...
- Строгие контракты. Добавить поле -- unchanged. Удалить -- требует деприкации.
Минусы:
- В браузере нужен gRPC-Web и grpc-gateway/Envoy.
- Бинарный payload нельзя посмотреть в Chrome DevTools без расширений.
- Тяжелее учиться, чем REST.
Когда WebSocket лучше
- Bidirectional низколатентный канал: чат, game state, collaboration (Figma, Google Docs), trading.
- Push с клиентских действий. Клиент шлёт команды, сервер стримит обновления.
- Long-lived сессия с много маленьких сообщений.
Минусы:
- Состояние на сервере (см. Stateful vs Stateless).
- Нет HTTP-кеша, нет retries, нет стандартного auth потока (JWT в первое сообщение или при handshake).
- Firewall/proxy иногда ломают WS.
- Балансировка сложнее (sticky или sharded).
Когда SSE лучше
- Один-направленный server push по HTTP. Обновления статуса, фид новостей, логи в браузер.
- Проще WebSocket: просто
EventSourceв JS, GET-endpoint на сервере. - Авто-переподключение встроено.
- Работает через HTTP-прокси.
- Не нужен bidi.
Минусы:
- Только server → client (для обратного направления нужен отдельный REST).
- Один EventSource = одно TCP-соединение; в HTTP/1.1 лимит 6 соединений на домен (в HTTP/2 не проблема).
- Нет бинарного протокола.
Сравнительная таблица
| Характеристика | REST | GraphQL | gRPC | WebSocket | SSE |
|---|---|---|---|---|---|
| Транспорт | HTTP 1/2 | HTTP 1/2 | HTTP/2 | TCP over HTTP upgrade | HTTP 1/2 |
| Формат | JSON | JSON | Protobuf (бинарный) | Любой (обычно JSON) | text/event-stream |
| Схема | OpenAPI (опционально) | SDL (обязательно) | .proto (обязательно) | Нет стандарта | Нет |
| Streaming | Нет (chunked transfer -- хак) | Subscriptions (over WS) | Да (4 модели) | Да (bidi) | Да (server → client) |
| Browser native | Да | Да | Нет (нужен gRPC-Web) | Да | Да (EventSource) |
| HTTP-кеш | Да | Сложно | Нет | Нет | Ограниченно |
| Payload size | Средний (JSON) | Средний | Маленький (protobuf) | Маленький (custom) | Средний (text) |
| Latency | Средняя | Средняя | Низкая (HTTP/2 mux) | Очень низкая | Низкая |
| Типизация | Слабая (runtime) | Строгая (schema) | Строгая (codegen) | Своя | Нет |
| Кривая обучения | Низкая | Средняя | Средняя-высокая | Средняя | Низкая |
| Лучшее применение | CRUD, публичный API | BFF, мобильные, сложные UI | Внутренние сервисы | Realtime bidi | Server push один в один |
Гибриды в реальном проекте
Типичный крупный стек:
Mobile / Web
│
▼
GraphQL / REST gateway (BFF)
│
▼
gRPC между сервисами (orders, users, payments)
│
▼
Postgres / Kafka / Redis
WebSocket -- отдельный сервис для чата.
SSE -- обновления уведомлений в веб.
GraphQL на периферии (клиент-facing), gRPC внутри (service-to-service), WS/SSE -- для стриминга.
Матрица выбора
| Сценарий | Рекомендация |
|---|---|
| Публичный API для внешних разработчиков | REST (OpenAPI) |
| Мобильный клиент, экономит payload | GraphQL или gRPC (если Android/iOS SDK) |
| Микросервисы внутри, полиглот | gRPC |
| Одностраничник с гибкими формами | GraphQL |
| Realtime чат, games | WebSocket |
| Dashboard с live-обновлением | SSE |
| Сервис с jerky UI (лайки, счётчики) | SSE или WS (вклад зависит от количества) |
| IoT телеметрия | MQTT / gRPC streaming |
| Internal CRUD admin tool | REST |
Код: один и тот же запрос пятью способами
Задача: получить пользователя по id.
<?php
declare(strict_types=1);
final class UsersController
{
public function __construct(private readonly UserService $users) {}
public function show(string $id): Response
{
$user = $this->users->find($id) ?? throw new NotFoundHttpException();
return new JsonResponse([
'id' => $user->id,
'email' => $user->email,
'name' => $user->name,
], 200, ['Cache-Control' => 'private, max-age=30']);
}
}
// GET /users/42 → {"id":"42","email":"a@b","name":"..."}
Редко проект обходится одним протоколом:
- REST + WS: админка + live-дашборд.
- GraphQL + gRPC: BFF на GraphQL, backend-to-backend на gRPC.
- REST + SSE: получить список задач (REST), подписаться на статус (SSE).
- gRPC + gRPC-streaming: обычные вызовы + телеметрия.
Антипаттерны
| Антипаттерн | Почему плохо |
|---|---|
| GraphQL где достаточно REST | Overengineering, N+1 без DataLoader |
| gRPC для публичного API | Браузеры без хака не умеют |
| WebSocket ради "модно" | Усложняет scale, auth, observability |
| REST + polling вместо SSE | Бешеная нагрузка при частом опросе |
| Версия через URL только, без deprecation | API превращается в зоопарк |
Выводы
- REST -- дефолт для публичных CRUD API, максимум инструментов и совместимости.
- GraphQL -- BFF для клиентов с гибкими потребностями (мобильные, сложные UI).
- gRPC -- внутренние сервисы, полиглот-стек, streaming, низкий overhead.
- WebSocket -- реалтайм bidirectional (чат, игры, collab).
- SSE -- дешёвый server push, когда не нужен обратный канал.
- В реальности используются вместе: REST/GraphQL снаружи, gRPC внутри, WS/SSE для realtime.
- Выбор -- по access pattern, не по моде.