Implementing Real-time Features
1. Setting Up WebSocket Server
Example: graphql-ws
import { useServer } from "graphql-ws/lib/use/ws";
import { WebSocketServer } from "ws";
const wss = new WebSocketServer({ server: httpServer, path: "/graphql" });
useServer({
schema,
context: async (ctx) => ({ user: await auth(ctx.connectionParams?.token), pubsub })
}, wss);
2. Managing Connection Lifecycle
| Hook | Use |
|---|---|
| onConnect | Authenticate, build context |
| onSubscribe | Per-operation auth/validation |
| onComplete | Cleanup per-subscription |
| onClose | Tear down connection-scoped resources |
| onPing/onPong | Keepalive |
3. Implementing Live Queries
| Approach | Detail |
|---|---|
| @live directive | Server re-pushes on change (graphql-live-query) |
| Polling | Client refetches at interval |
| Subscription wrap | Subscribe to entity changes, refetch |
4. Using Server-sent Events
Example: graphql-sse
import { createHandler } from "graphql-sse/lib/use/express";
app.use("/graphql/stream", createHandler({ schema }));
| SSE vs WebSocket | Detail |
|---|---|
| Direction | Server-to-client only |
| Reconnect | Automatic (browser-native EventSource) |
| Proxies | Works through any HTTP/1.1 proxy |
5. Implementing Presence Features
| Pattern | Detail |
|---|---|
| Online users set | Redis SET per channel |
| Heartbeat | Client emits every 30s; server expires after 60s |
| Subscription | presenceChanged(channelId) |
6. Broadcasting Events
Example: Redis-backed PubSub
import { RedisPubSub } from "graphql-redis-subscriptions";
const pubsub = new RedisPubSub({
publisher: new Redis(REDIS_URL),
subscriber: new Redis(REDIS_URL)
});
7. Filtering Real-time Updates
| Where | Detail |
|---|---|
| withFilter | Per-event predicate |
| Topic naming | Include shard key (e.g., room:42:msg) |
| Auth | Re-check on every event (permissions may revoke) |
8. Implementing Event Sourcing
| Aspect | Detail |
|---|---|
| Source events | Domain events stored append-only |
| Subscriptions | Project events into GraphQL streams |
| Replay | New subscribers can fetch from cursor |
9. Handling Connection Drops
| Strategy | Detail |
|---|---|
| Client retry | Exponential backoff with jitter |
| Resume cursor | Send lastEventId on reconnect |
| Idempotent events | Replays must be safe |
10. Scaling Real-time Services
| Layer | Detail |
|---|---|
| Pub/Sub backbone | Redis, NATS, Kafka for cross-node fanout |
| Sticky sessions | WebSocket needs session affinity at LB |
| Connection limit | Tune ulimit, file descriptors |
| Sharding | Partition channels across nodes |