Real-Time GraphQL Subscriptions with WebSockets0%

Real-Time GraphQL Subscriptions with WebSockets

Beginner12 min read•Updated: Jul 11, 2026
Study MaterialsCheatsheet

Real-Time GraphQL Subscriptions with WebSockets

While queries and mutations follow the classic request-response cycle, real-time enterprise features (such as live trading monitors, collaborative chat, and real-time document notifications) require bidirectional push communication. GraphQL Subscriptions maintain a persistent WebSocket connection between client and server, streaming events to React components using useSubscription.


1. GraphQL Transport Layer Architecture

Modern Apollo Client architectures use a split-link configuration:

  • HTTP Link (httpLink): Handles standard Queries and Mutations over HTTP/HTTPS.
  • WebSocket Link (wsLink): Handles persistent Subscriptions using the standard graphql-ws library.
Output
React Application
│
▼
Apollo Split Link (isSubscription?)
├─── Yes ──► GraphQL-WS Link ──► wss://api.enterprise.com/graphql
└─── No ──► HTTP Link ──► https://api.enterprise.com/graphql

Bash / Terminal
npm install graphql-ws
TypeScript
// src/lib/apolloClient.ts
import { ApolloClient, InMemoryCache, split, HttpLink } from "@apollo/client";
import { GraphQLWsLink } from "@apollo/client/link/subscriptions";
import { createClient } from "graphql-ws";
import { getMainDefinition } from "@apollo/client/utilities";
 
// 1. Standard HTTP Link for Queries & Mutations
const httpLink = new HttpLink({
uri: "https://api.enterprise.com/graphql",
});
 
// 2. WebSocket Link for Subscriptions
const wsLink = new GraphQLWsLink(
createClient({
url: "wss://api.enterprise.com/graphql",
connectionParams: () => ({
authToken: sessionStorage.getItem("access_token"),
}),
})
);
 
// 3. Directional Split: route subscription operations to WebSocket
const splitLink = split(
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === "OperationDefinition" &&
definition.operation === "subscription"
);
},
wsLink,
httpLink
);
 
export const client = new ApolloClient({
link: splitLink,
cache: new InMemoryCache(),
});

3. Consuming Live Events with useSubscription

TSX
import React, { useState } from "react";
import { gql, useSubscription } from "@apollo/client";
 
const LIVE_SECURITY_ALERTS = gql`
subscription OnSecurityAlert($clusterId: ID!) {
securityAlertAdded(clusterId: $clusterId) {
id
severity
message
timestamp
}
}
`;
 
interface Alert {
id: string;
severity: "LOW" | "HIGH" | "CRITICAL";
message: string;
timestamp: string;
}
 
export function LiveSecurityFeed({ clusterId }: { clusterId: string }) {
const [alerts, setAlerts] = useState<Alert[]>([]);
 
// Subscribes to incoming live WebSocket events
useSubscription(LIVE_SECURITY_ALERTS, {
variables: { clusterId },
onData: ({ data }) => {
const newAlert = data.data?.securityAlertAdded;
if (newAlert) {
setAlerts((prev) => [newAlert, ...prev.slice(0, 49)]); // Keep last 50
}
},
});
 
return (
<div className="p-6 bg-slate-900 border border-slate-800 rounded-xl text-white">
<div className="flex items-center justify-between mb-4">
<h2 className="font-bold text-lg">Live Telemetry & Security Alerts</h2>
<span className="flex items-center gap-2 text-xs text-emerald-400 font-mono">
<span className="h-2 w-2 rounded-full bg-emerald-400 animate-ping" />
WebSocket Connected
</span>
</div>
 
<div className="space-y-2 max-h-80 overflow-y-auto">
{alerts.length === 0 ? (
<p className="text-xs text-slate-500 italic">Listening for cluster events...</p>
) : (
alerts.map((alert) => (
<div
key={alert.id}
className="p-3 bg-slate-950 border border-slate-800 rounded flex justify-between items-center text-xs"
>
<div className="flex items-center gap-2">
<span
className={`font-bold px-1.5 py-0.5 rounded ${
alert.severity === "CRITICAL"
? "bg-rose-950 text-rose-400 border border-rose-800"
: "bg-amber-950 text-amber-400"
}`}
>
{alert.severity}
</span>
<span className="text-slate-200">{alert.message}</span>
</div>
<span className="text-slate-500 font-mono">{alert.timestamp}</span>
</div>
))
)}
</div>
</div>
);
}

4. Subscriptions vs Polling

Avoid using subscriptions for data that changes infrequently. For standard status checks (e.g. tracking a background export progress), simple query polling (pollInterval: 5000) avoids the overhead of managing long-lived WebSocket connections and socket reconnections.


Interactive Quiz

Practice Quiz

Question 1
What protocol does Apollo Client use to maintain persistent GraphQL Subscriptions?
Question 2
What is the purpose of the split function in Apollo Client configuration?
Question 3
What is the current standard library for GraphQL WebSockets recommended over the deprecated subscriptions-transport-ws?
Question 4
How do you authenticate a WebSocket connection in graphql-ws?
Question 5
When should you prefer query polling (pollInterval) over GraphQL Subscriptions?
Next Lesson

WebSockets Architecture with Socket.io in React

Continue learning with hands-on practice, examples, and exercises in the upcoming topic.

Practice Quiz

Test your understanding of this lesson with 5 questions. Each question has one correct answer.

PrevNext