Прямой ответ
GraphQL subscriptions — механизм realtime-данных в GraphQL (GraphQL — «подписка на события» — GraphQL — «клиент подписывается → сервер шлёт обновления» — realtime — «не polling, а push» — realtime); зачем subscriptions: realtime без polling (GraphQL — «подписка → обновления» — GraphQL — «не опрашивать, а получать» — realtime), типизация (GraphQL — «схема определяет подписки» — GraphQL — «subscription UserCreated: User» — GraphQL), интеграция с GraphQL (GraphQL — «подписки — часть GraphQL» — GraphQL — «один API — queries/mutations/subscriptions» — GraphQL); транспорт (WebSocket): subscriptions работают через WebSocket (WebSocket — «subscriptions → WebSocket» — WebSocket — graphql-ws протокол (WebSocket — «graphql-ws over WebSocket» — WebSocket — «не HTTP, а WebSocket» — WebSocket); как работает: (1) клиент отправляет subscription-запрос (GraphQL — «subscription { userCreated { id name } }» — GraphQL), (2) сервер регистрирует подписку (GraphQL — «клиент подписался» — GraphQL), (3) событие произошло (GraphQL — «UserCreated» — GraphQL — event-driven), (4) сервер шлёт данные клиенту (GraphQL — «событие → данные клиенту» — GraphQL — realtime), (5) клиент получает обновления (GraphQL — «клиент получил» — GraphQL — realtime); серверные реализации: (1) Strawberry (Python — Strawberry — «subscriptions в Strawberry» — Strawberry — async generators (Strawberry — «async def user_created(): yield user» — Strawberry); (2) Apollo Server (Node.js — Apollo — «subscriptions в Apollo» — Apollo — PubSub (Apollo — «PubSub — pub/sub» — Apollo — Redis); (3) Graphene (Python — Graphene — «subscriptions в Graphene» — Graphene — async generators (Graphene — «async def user_created(): yield user» — Graphene); PubSub (pub/sub для subscriptions): сервер публикует события (event-driven — «событие → PubSub» — event-driven), подписчики получают (event-driven — «PubSub → подписчики» — event-driven — Redis/Kafka (Redis — «PubSub через Redis» — Redis — event-driven); когда subscriptions: realtime-данные (GraphQL — «уведомления/обновления» — GraphQL — realtime), чат (WebSocket — «чат — subscriptions» — WebSocket — realtime), дашборды (GraphQL — «дашборд — обновления» — GraphQL — realtime); когда НЕ subscriptions: редкие обновления (GraphQL — «раз в минуту — polling» — GraphQL — «subscriptions оверхед» — техдолг), простые случаи (GraphQL — «queries достаточно» — GraphQL — «subscriptions не нужны» — техдолг); антипаттерны: subscriptions без PubSub (GraphQL — «subscriptions без PubSub» — GraphQL — «событие не доходит» — event-driven — PubSub обязателен — event-driven), subscriptions для редких событий (GraphQL — «subscriptions раз в минуту» — GraphQL — «polling проще» — техдолг), subscriptions без reconnect (WebSocket — «WebSocket упал — не переподключился» — WebSocket — reconnect обязателен — realtime).
Практика: subscriptions в Strawberry (Python)
Strawberry subscriptions (Strawberry: import strawberry from typing import AsyncGenerator @strawberry.type class Subscription: @strawberry.subscription async def user_created(self) -> AsyncGenerator[User, None]: async for user in pubsub.listen("USER_CREATED"): yield user (Strawberry — «subscription в Strawberry» — Strawberry — async generator (Strawberry — «yield user» — Strawberry); PubSub (Redis — через Redis): import redis.asyncio as redis redis_client = redis.Redis() pubsub = redis_client.pubsub() await pubsub.subscribe("USER_CREATED") (Redis — «PubSub через Redis» — Redis — event-driven); Публикация события (event-driven: await redis_client.publish("USER_CREATED", json.dumps({"id": user.id, "name": user.name})) (event-driven — «событие → PubSub» — event-driven — Redis); Apollo Server (Node.js — Apollo: const { PubSub } = require("graphql-subscriptions"); const pubsub = new PubSub(); const resolvers = { Subscription: { userCreated: { subscribe: () => pubsub.asyncIterator(["USER_CREATED"]) } } }; (Apollo — «subscriptions в Apollo» — Apollo — PubSub (Apollo — event-driven); Мониторинг (метрики: «subscription connections», «events published» — метрики — алерты — алерты — «подписки падают» — incident).
Практический сценарий: уведомления через GraphQL subscriptions
Контекст: веб-приложение на GraphQL (GraphQL), нужно добавить realtime-уведомления (realtime — «новое уведомление → сразу клиенту» — realtime), боль: polling (GraphQL — «опрашивать каждые 5 с» — GraphQL — оверхед — техдолг); решение — subscriptions: (1) схема (GraphQL — type Subscription { notificationCreated: Notification } (GraphQL — «subscription в схеме» — GraphQL); (2) Strawberry (Strawberry — @strawberry.subscription async def notification_created(self) -> AsyncGenerator[Notification, None]: async for notif in pubsub.listen("NOTIFICATION_CREATED"): yield notif (Strawberry — «subscription в Strawberry» — Strawberry); (3) PubSub (Redis — Redis PubSub (Redis — «PubSub через Redis» — Redis — event-driven); (4) публикация (event-driven — await redis.publish("NOTIFICATION_CREATED", json.dumps(notif)) (event-driven — «событие → PubSub» — event-driven); (5) клиент (GraphQL — subscription { notificationCreated { id message } } (GraphQL — «клиент подписался» — GraphQL — realtime); (6) WebSocket (WebSocket — graphql-ws (WebSocket — «subscriptions → WebSocket» — WebSocket — reconnect (WebSocket — «WebSocket упал — reconnect» — WebSocket); (7) мониторинг (метрики — «subscription connections» — метрики — алерты — алерты); результат: уведомление → сразу клиенту (realtime — «не polling, а push» — realtime — PubSub (event-driven — «событие → PubSub → клиент» — event-driven — WebSocket (WebSocket — «subscriptions → WebSocket» — WebSocket); вывод: subscriptions решили задачу (realtime без polling — realtime — ADR — «subscriptions без PubSub» — антипаттерн — техдолг).
FAQ
GraphQL subscriptions против WebSocket — в чём разница? GraphQL subscriptions (GraphQL — «подписки в GraphQL» — GraphQL — работают через WebSocket (WebSocket — «subscriptions → WebSocket» — WebSocket — graphql-ws (WebSocket — «graphql-ws over WebSocket» — WebSocket); WebSocket (WebSocket — «чистый WebSocket» — WebSocket — низкоуровневый (WebSocket — «WebSocket — транспорт» — WebSocket — «subscriptions — протокол поверх» — WebSocket); когда subscriptions: GraphQL API (GraphQL — «subscriptions — часть GraphQL» — GraphQL — типизация (GraphQL — «схема определяет» — GraphQL), когда WebSocket: не-GraphQL (WebSocket — «чистый WebSocket» — WebSocket — «без GraphQL» — WebSocket); «subscriptions без WebSocket» — нет (subscriptions → WebSocket — WebSocket — ADR — техдолг).
Что такое PubSub и зачем он нужен для subscriptions? PubSub (event-driven — pub/sub — event-driven — «publish/subscribe» — event-driven — издатель публикует события (event-driven — «событие → PubSub» — event-driven — подписчики получают (event-driven — «PubSub → подписчики» — event-driven); зачем: subscriptions без PubSub не работают (GraphQL — «событие не доходит» — event-driven — PubSub обязателен — event-driven — Redis/Kafka (Redis — «PubSub через Redis» — Redis — event-driven); реализация: Redis PubSub (Redis — «Redis PubSub» — Redis — event-driven), Kafka (event-driven — «Kafka — pub/sub» — event-driven — Kafka); «subscriptions без PubSub» — антипаттерн (событие не доходит — event-driven — PubSub обязателен — event-driven — ADR — техдолг).
Как масштабировать GraphQL subscriptions на несколько серверов? Проблема: subscriptions stateful (GraphQL — «подписка на сервере 1» — GraphQL — «событие на сервере 2» — event-driven — «клиент не получит» — realtime); решение: PubSub (event-driven — Redis/Kafka (Redis — «событие → PubSub → все серверы» — event-driven — Redis — «сервер 1 публикует, сервер 2 получает» — event-driven); Redis PubSub (Redis — «Redis PubSub» — Redis — «все серверы подписаны» — Redis — event-driven); «масштабирование без PubSub» — не работает (событие на сервере 1 — клиент на сервере 2 не получит — event-driven — PubSub обязателен — Redis — ADR — техдолг).
GraphQL subscriptions против SSE — что выбрать? GraphQL subscriptions (GraphQL — «подписки в GraphQL» — GraphQL — WebSocket (WebSocket — «subscriptions → WebSocket» — WebSocket — двунаправленный (WebSocket — «WebSocket — двунаправленный» — WebSocket); SSE (SSE — «SSE — однонаправленный» — SSE — HTTP (SSE — «SSE — HTTP» — SSE — авто-reconnect (SSE — «SSE сам» — SSE); когда subscriptions: GraphQL API (GraphQL — «subscriptions — часть GraphQL» — GraphQL — двунаправленный (WebSocket — «WebSocket — двунаправленный» — WebSocket), когда SSE: однонаправленный (SSE — «SSE — однонаправленный» — SSE — проще (SSE — «SSE проще» — SSE); «subscriptions для однонаправленных» — антипаттерн (SSE проще — SSE — ADR — техдолг).
Источники: GraphQL docs (subscriptions), Apollo Server docs, Strawberry docs — ссылки в шапке.