Стили API: REST, RPC, GraphQL, WebSocket, SSE

↑ 1.1 Сети и протоколы для бэкенда · ← Предыдущая · → Следующая

Желательно~6 мин чтенияУровень: junior

Зачем это на собесе

«Как бы вы сделали API?» — стартовый вопрос почти любого интервью. Нужно уметь выбрать стиль под задачу и объяснить компромиссы: REST для публичных CRUD-API, gRPC между сервисами, GraphQL для гибких клиентских выборок, WebSocket и SSE для push.

Подтемы

  • REST: ресурсы, HATEOAS, ограничения
  • RPC и gRPC
  • GraphQL
  • WebSocket
  • SSE (Server-Sent Events)
  • Как выбрать стиль

Объяснение

Сравнение

СтильТранспортФорматНаправлениеСильная сторонаСлабая сторона
RESTHTTP/1.1+JSONзапрос → ответпростота, кэш, инструментыover/under-fetching, много запросов
gRPCHTTP/2Protobufзапрос-ответ + стримыскорость, строгие контракты, codegenне работает напрямую из браузера (нужен gRPC-Web), сложнее отладка
GraphQLHTTP (POST)JSONзапрос → ответ (+ subscriptions)клиент сам выбирает поля, один endpointN+1, сложный кэш и авторизация на полях, защита от тяжёлых запросов
WebSocketTCP (upgrade из HTTP)любойдвустороннийнизкая задержка, полный дуплексstateful, масштабирование и балансировка сложнее
SSEHTTPтекст (text/event-stream)сервер → клиентпросто, авто-реконнект, работает через HTTPтолько в одну сторону, лимит соединений в HTTP/1.1

REST

  • Ресурсы — существительные (/patients/42/visits), действие задаёт метод HTTP.
  • Stateless: каждый запрос самодостаточен, состояние сессии не на сервере.
  • Единообразие: коды статусов, форматы ошибок (ProblemDetails), пагинация, фильтры.
  • Уровни зрелости Ричардсона: ресурсы → HTTP-методы → HATEOAS (гиперссылки, на практике редко).

RPC / gRPC

  • Вызов удалённой процедуры: GetPatient(id), а не «GET ресурса».
  • gRPC: контракт в .proto, Protobuf компактнее JSON, HTTP/2 мультиплексирует, есть unary, server/client/bidirectional streaming, deadlines и отмена.
  • Идеально для внутренней связи микросервисов.

GraphQL

  • Одна точка входа, клиент описывает нужную форму ответа.
  • Схема со строгой типизацией; query, mutation, subscription.
  • Риски: N+1 (лечат DataLoader), «дорогие» запросы (лимиты глубины и сложности), кэширование сложнее HTTP-кэша.

WebSocket

  • Начинается как HTTP-запрос с Upgrade: websocket, затем это двусторонний поток фреймов поверх одного TCP.
  • Чаты, котировки, совместное редактирование. В .NET — SignalR (автоматически выбирает транспорт и переподключается).

SSE

  • Обычный HTTP-ответ, который не закрывается: Content-Type: text/event-stream.
  • Формат: строки data: ... и пустая строка между событиями; id: для Last-Event-ID при реконнекте.
  • Подходит для лент уведомлений, прогресса задач, стриминга ответов LLM.

Примеры

// REST: Minimal API
app.MapGet("/api/patients/{id:guid}", async (Guid id, IPatientService svc, CancellationToken ct) =>
    await svc.FindAsync(id, ct) is { } p ? Results.Ok(p) : Results.NotFound());
// SSE в ASP.NET Core (.NET 10: TypedResults.ServerSentEvents; до этого — вручную)
app.MapGet("/api/jobs/{id}/events", async (string id, HttpContext ctx, IJobEvents events, CancellationToken ct) =>
{
    ctx.Response.Headers.ContentType = "text/event-stream";
    await foreach (var e in events.SubscribeAsync(id, ct))
    {
        await ctx.Response.WriteAsync($"id: {e.Id}\ndata: {e.Json}\n\n", ct);
        await ctx.Response.Body.FlushAsync(ct);
    }
});
// WebSocket через SignalR
public class ChatHub : Hub
{
    public Task Send(string room, string text) =>
        Clients.Group(room).SendAsync("message", Context.UserIdentifier, text);
}
// gRPC контракт
service Patients { rpc Get (GetPatientRequest) returns (Patient); }

Нюансы и подводные камни

  • WebSocket и SSE держат соединения открытыми: nginx нужны proxy_http_version 1.1, заголовки Upgrade/Connection (для WS), proxy_buffering off (для SSE) и большие proxy_read_timeout.
  • За L7-балансировщиком WebSocket требует sticky-сессий или общего backplane (Redis) для SignalR при нескольких инстансах.
  • В HTTP/1.1 браузер разрешает ~6 соединений на домен — несколько SSE-потоков могут исчерпать лимит; HTTP/2 снимает проблему.
  • gRPC из браузера — только gRPC-Web с прокси (Envoy).
  • Не выбирайте GraphQL «потому что модно»: для простого CRUD REST дешевле в поддержке.

Как выбрать

  • Публичный API для внешних клиентов, кэш и простота → REST.
  • Быстрая внутренняя связь между сервисами, стриминг → gRPC.
  • Много разнородных клиентов с разными потребностями в данных → GraphQL (часто как BFF-слой).
  • Двусторонний realtime → WebSocket/SignalR.
  • Односторонние уведомления → SSE.

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

Связанные темы