Subscriptions na spec GraphQL
Subscription é a terceira operation root (além de Query e Mutation). Retorna um stream de eventos ao invés de um valor único. Transport padrão hoje é WebSocket via protocolo graphql-ws, com alternativa SSE via graphql-sse.
type Subscription {
messageAdded(channelId: ID!): Message!
postUpdated(postId: ID!): Post!
}Servidor com graphql-ws
import { WebSocketServer } from 'ws';
import { useServer } from 'graphql-ws/lib/use/ws';
import { createPubSub } from 'graphql-yoga';
const pubsub = createPubSub();
const resolvers = {
Subscription: {
messageAdded: {
subscribe: (_, { channelId }) =>
pubsub.subscribe('message:' + channelId),
resolve: (payload) => payload,
},
},
Mutation: {
sendMessage: async (_, { channelId, text }, ctx) => {
const msg = await ctx.db.message.create({ channelId, text, userId: ctx.user.id });
pubsub.publish('message:' + channelId, msg);
return msg;
},
},
};
const wsServer = new WebSocketServer({ server: httpServer, path: '/graphql' });
useServer({ schema, context: authContext }, wsServer);Cliente com Apollo Client
import { split, HttpLink } from '@apollo/client';
import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
import { createClient } from 'graphql-ws';
import { getMainDefinition } from '@apollo/client/utilities';
const wsLink = new GraphQLWsLink(createClient({
url: 'wss://api.exemplo.com/graphql',
connectionParams: () => ({ authToken: getToken() }),
retryAttempts: Infinity,
}));
const link = split(
({ query }) => {
const def = getMainDefinition(query);
return def.kind === 'OperationDefinition' && def.operation === 'subscription';
},
wsLink,
httpLink,
);Auth em WebSocket: mande o token no connectionParams, valide no onConnect do servidor. Nunca dependa de cookie — muitos clientes WS não enviam. Rotacione token em reconexão.
Scaling cross-instance
Redis pubsub (graphql-redis-subscriptions) é o padrão: cada pod publica e assina pelo Redis. NATS e Kafka servem quando há necessidade de persistência ou replay. Filtre fan-out: um tópico por canal, não broadcast global.
Backpressure importa: cliente lento com muitos eventos vaza memória no servidor. Feche socket após N eventos pendentes ou use bounded channel por cliente.