graphql

GraphQL Subscriptions: Real-Time Data with WebSockets

Muhammad Naeem
March 30, 2025
14 min read
GraphQL Subscriptions: Real-Time Data with WebSockets

Implement real-time features with GraphQL subscriptions. Learn WebSocket setup, pub/sub patterns, and scaling strategies.

GraphQL subscriptions enable real-time, event-driven communication between clients and servers using WebSockets. Unlike queries and mutations that use request-response patterns, subscriptions maintain persistent connections, allowing servers to push updates to clients immediately. This guide covers implementing subscriptions from basic setup to production-ready patterns, including pub/sub systems, authentication, and scaling strategies for real-time applications.

📚 Table of Contents

1. Understanding GraphQL Subscriptions2. Setting Up Subscriptions Server3. Implementing Subscription Resolvers4. Client-Side Implementation5. Authentication and Authorization6. Scaling Subscriptions7. Best Practices and Patterns

Understanding GraphQL Subscriptions

Subscriptions complement GraphQL queries and mutations for real-time features. Clients subscribe to events by sending subscription queries over WebSocket connections. Servers push updates when events occur.

This is perfect for chat applications, live notifications, collaborative editing, real-time dashboards, or any feature requiring instant updates. Subscriptions use WebSocket protocol for bidirectional communication. The connection stays open, reducing latency compared to polling.

Implement subscriptions for actual real-time needs - don't use them when periodic polling suffices. Consider server resources - subscriptions maintain persistent connections, consuming memory and CPU.

Setting Up Subscriptions Server

For Apollo Server, install graphql-subscriptions and subscription transport library. Use makeExecutableSchema to create schema with Subscription type. Define subscription resolvers that return AsyncIterator.

Implement pub/sub system using PubSub from graphql-subscriptions. In production, use Redis or other scalable pub/sub instead of in-memory PubSub. Configure WebSocket server alongside HTTP server.

Apollo Server handles WebSocket upgrade requests automatically when configured. For Express apps, create separate HTTP server. Enable CORS for WebSocket connections.

Implement connection initialization for authentication. Handle connection lifecycle events - connect, disconnect, error. Test subscriptions using GraphQL Playground or Apollo Studio.

Implementing Subscription Resolvers

Subscription resolvers return AsyncIterator that yields data when events occur. Use pubsub.asyncIterator to create iterators subscribed to specific topics. Trigger events using pubsub.publish with topic name and payload.

Subscription resolvers can accept arguments for filtering. Implement subscribe function to set up subscription and optionally resolve function to transform data. Keep subscription logic lightweight - expensive operations should happen in publish events, not subscriptions.

Use typed TypeScript AsyncIterators for type safety. Implement proper error handling in subscription resolvers. Clean up resources when subscriptions close.

Consider using withFilter to filter subscription events based on subscription arguments.

Client-Side Implementation

On the client, use Apollo Client with WebSocket link for subscriptions. Install @apollo/client and graphql-ws. Create WebSocket link using GraphQLWsLink pointing to your WebSocket endpoint.

Use split function to route subscription operations through WebSocket link and queries/mutations through HTTP link. Call useSubscription hook in React components to subscribe. Hook returns data, loading, and error states.

Subscription connection opens when component mounts and closes on unmount. Handle reconnection logic for network failures. Implement subscription batching to reduce connection overhead.

Use subscription-transport-ws for older implementations or graphql-ws for newer projects. Test subscriptions with mock server or actual WebSocket endpoint.

Authentication and Authorization

Authenticate subscriptions during connection initialization. Send authentication token in connection params when establishing WebSocket connection. Server validates token in onConnect hook before accepting connection.

Store user context in connection params for use in subscription resolvers. Implement field-level authorization in subscription resolvers checking user permissions. For sensitive data, verify authorization on each subscription event, not just connection.

Consider implementing subscription-specific permissions separate from query/mutation permissions. Use secure WebSocket (wss://) in production. Implement proper error handling for authentication failures.

Clean up subscriptions properly when users log out.

Scaling Subscriptions

In-memory PubSub doesn't work across multiple servers. Use Redis pub/sub for distributed systems. Install graphql-redis-subscriptions and create RedisPubSub instance.

Configure Redis connection with high availability setup. Use Redis Cluster for horizontal scaling. Consider using managed Redis services like AWS ElastiCache.

Implement connection draining during deployments to avoid dropping active subscriptions. Use sticky sessions or connection state synchronization for load balancing. Monitor active subscription count and server resources.

Implement connection limits per client to prevent abuse. Consider using serverless or edge solutions for global distribution. Test scaling setup under realistic load.

Best Practices and Patterns

Design subscription events around business domain events, not database changes. Keep payload sizes small - clients can query for details. Implement subscription throttling to prevent overwhelming clients.

Use debouncing for rapidly changing data. Clean up subscriptions properly - implement disconnect handlers. Monitor subscription count and connection durations.

Implement heartbeat/ping-pong for connection health. Handle reconnection gracefully with exponential backoff. Consider using subscription filters to reduce unnecessary events.

Document subscription schemas clearly. Implement proper error handling and logging. Test subscriptions thoroughly including connection failures.

Consider implementing subscription deprecation strategy. Monitor WebSocket connection stability and debug issues proactively.

💡 Key Takeaways

GraphQL subscriptions enable powerful real-time features with clean, type-safe APIs. While more complex than polling, subscriptions provide better user experience and resource efficiency for true real-time use cases.

Conclusion

GraphQL subscriptions enable powerful real-time features with clean, type-safe APIs. While more complex than polling, subscriptions provide better user experience and resource efficiency for true real-time use cases. Success requires proper server setup, scalable pub/sub systems, authentication, and careful monitoring. Start simple with in-memory pub/sub for development, then move to Redis or similar for production. Balance real-time features against complexity and resource costs. Not every feature needs subscriptions - use them where instant updates matter. With proper implementation, GraphQL subscriptions enable building responsive, real-time applications that delight users.

Tags
GraphQL
Subscriptions
WebSockets
Real-time
Continue Reading
NestJS Guards, Interceptors, and Pipes: Request Processing