GraphQL Subscriptions Client — Building Real-Time Features with WebSockets
In this tutorial, you will learn about Graphql Subscriptions Client. We cover key concepts, practical examples, and best practices to help you master this topic.
GraphQL subscriptions clients connect to servers via WebSockets to receive real-time updates, enabling live dashboards, notifications, and collaborative features without polling.
What You'll Learn
- Websocket connection lifecycle for subscriptions
- Using Apollo Client with subscriptions-transport-ws
- Reconnection and error handling
- Subscribing to multiple channels
- Updating UI state from subscription events
- Subscription authentication
Why It Matters
Real-time features like live threat alerts, device status updates, and scan progress notifications require the server to push data to clients. Subscriptions provide a standardized GraphQL-native way to do this. DodaTech's Durga Antivirus Pro uses subscriptions to push real-time threat alerts to admin dashboards — when a new threat is detected across any of 10 million devices, administrators see it within 200ms.
Real-World Use
A security operations center displays a live feed of incoming threats. Each threat appears on the dashboard as it's detected, with sound alerts for critical severity. The subscription also updates device status indicators when devices go offline or complete a scan.
sequenceDiagram
participant Client
participant WebSocket
participant Server
participant EventSource
Client->>WebSocket: GQL_CONNECTION_INIT
WebSocket->>Server: connection_init (auth)
Server-->>WebSocket: GQL_CONNECTION_ACK
Client->>WebSocket: GQL_START (threatAlerted)
WebSocket->>Server: subscribe threatAlerted
Server->>EventSource: Register listener
EventSource-->>Server: New threat detected
Server-->>WebSocket: GQL_DATA (threat)
WebSocket-->>Client: data: { threatAlerted: {...} }
Client->>Client: Update UI
Code Examples
Example 1: Apollo Client with Subscriptions
import { ApolloClient, InMemoryCache, gql } from '@apollo/client';
import { WebSocketLink } from '@apollo/client/link/ws';
import { split, HttpLink } from '@apollo/client';
import { getMainDefinition } from '@apollo/client/utilities';
const httpLink = new HttpLink({
uri: 'https://api.dodatech.com/graphql',
});
const wsLink = new WebSocketLink({
uri: 'wss://api.dodatech.com/graphql',
options: {
reconnect: true,
connectionParams: {
authToken: localStorage.getItem('token'),
},
},
});
const link = split(
({ query }) => {
const def = getMainDefinition(query);
return def.kind === 'OperationDefinition'
&& def.operation === 'subscription';
},
wsLink,
httpLink,
);
const client = new ApolloClient({
link,
cache: new InMemoryCache(),
});
Example 2: React Component with useSubscription
import { gql, useSubscription } from '@apollo/client';
const THREAT_ALERTED = gql`
subscription ThreatAlerted {
threatAlerted {
id
deviceName
threatName
severity
detectedAt
}
}
`;
function ThreatDashboard() {
const { data, loading, error } = useSubscription(THREAT_ALERTED);
useEffect(() => {
if (data?.threatAlerted) {
const alert = data.threatAlerted;
if (alert.severity === 'CRITICAL') {
playAlertSound();
}
showNotification(`Threat detected: ${alert.threatName}`);
}
}, [data]);
if (error) return <div>Subscription error: {error.message}</div>;
if (loading) return <div>Connecting...</div>;
return (
<div>
<h2>Live Threat Feed</h2>
{data && (
<div className="alert">
<strong>{data.threatAlerted.threatName}</strong>
<span className={`severity-${data.threatAlerted.severity.toLowerCase()}`}>
{data.threatAlerted.severity}
</span>
</div>
)}
</div>
);
}
Example 3: Multiple Subscriptions and Reconnection
import { ApolloClient, gql } from '@apollo/client';
import { WebSocketLink } from '@apollo/client/link/ws';
function createSubscriptionClient() {
const wsLink = new WebSocketLink({
uri: 'wss://api.dodatech.com/graphql',
options: {
reconnect: true,
reconnectionAttempts: 10,
connectionCallback: (error) => {
if (error) {
console.error('WebSocket error:', error);
}
},
},
});
const client = new ApolloClient({
link: wsLink,
cache: new InMemoryCache(),
});
// Subscribe to multiple channels
const subscriptions = [
client.subscribe({
query: gql`subscription { threatAlerted { id threatName severity } }`,
}),
client.subscribe({
query: gql`subscription { deviceStatusChanged { id name status } }`,
}),
client.subscribe({
query: gql`subscription { scanCompleted { deviceId scanId result } }`,
}),
];
return { client, subscriptions };
}
const { subscriptions } = createSubscriptionClient();
subscriptions.forEach((sub, index) => {
sub.subscribe({
next: (data) => {
console.log(`Subscription ${index} data:`, data);
// Update application state
},
error: (err) => {
console.error(`Subscription ${index} error:`, err);
},
complete: () => {
console.log(`Subscription ${index} completed`);
},
});
});
Common Mistakes
- Not handling reconnection — WebSocket connections drop. Implement automatic reconnection with exponential backoff and resubscribe to all channels.
- Sending auth tokens in URL params — tokens can leak in server logs. Send auth in the connection payload instead.
- Subscribing without unsubscribing — each subscription holds server resources. Unsubscribe in component cleanup or when navigating away.
- Ignoring subscription errors — network errors, auth expiry, and server restarts all cause subscriptions to fail. Show connection status to users.
- Using subscriptions for one-time events — subscriptions are for continuous streams. Use queries or mutations for one-time data fetching.
Practice Questions
- How does the split link determine whether to use HTTP or WebSocket?
- What is the WebSocket connection lifecycle for subscriptions?
- How do you authenticate a subscription connection?
- Why should you unsubscribe from subscriptions when a component unmounts?
- What happens when a WebSocket connection drops and reconnects?
Challenge: Build a real-time monitoring dashboard with three subscriptions: threat alerts (pushed to all admins), device status changes (filtered by region), and scan progress (per-user). Handle reconnection and show connection state to the user.
Mini Project
Build a React application with real-time threat monitoring using GraphQL subscriptions. Include connection status indicator, sound alerts for critical threats, device status grid that updates live, and automatic reconnection with exponential backoff.
FAQ
What's Next
Learn advanced subscription patterns and filtering
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro