Skip to content

GraphQL Subscriptions Client — Building Real-Time Features with WebSockets

DodaTech Updated 2026-06-28 4 min read

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

  1. Not handling reconnection — WebSocket connections drop. Implement automatic reconnection with exponential backoff and resubscribe to all channels.
  2. Sending auth tokens in URL params — tokens can leak in server logs. Send auth in the connection payload instead.
  3. Subscribing without unsubscribing — each subscription holds server resources. Unsubscribe in component cleanup or when navigating away.
  4. Ignoring subscription errors — network errors, auth expiry, and server restarts all cause subscriptions to fail. Show connection status to users.
  5. Using subscriptions for one-time events — subscriptions are for continuous streams. Use queries or mutations for one-time data fetching.

Practice Questions

  1. How does the split link determine whether to use HTTP or WebSocket?
  2. What is the WebSocket connection lifecycle for subscriptions?
  3. How do you authenticate a subscription connection?
  4. Why should you unsubscribe from subscriptions when a component unmounts?
  5. 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

Can I use subscriptions with HTTP/2?

Subscriptions require a persistent connection. They work over WebSocket or Server-Sent Events, not standard HTTP. WebSocket is the most common transport.

How many subscriptions can a single client open?

There's no hard limit, but each subscription holds memory and a file descriptor on the server. Most servers handle thousands of subscriptions per client, but keep it reasonable (10-20).

Do subscriptions work with Apollo Client cache?

Yes. Subscription results can update the Apollo Client cache automatically. Use updateQueries or cache.modify to merge subscription data.

How do I filter subscription events on the server?

Use subscription arguments or context-based filtering. For example, subscribe to threatAlerted(region: 'US') to only receive alerts for US-based devices.

What happens to subscriptions during a server restart?

All WebSocket connections drop. Clients must reconnect and resubscribe. The server should handle this gracefully.

What's Next

Learn advanced subscription patterns and filtering

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro