Skip to Content
04 AdvancedSSE Subscriptions

SSE Subscriptions

Server-Sent Events (SSE) provide a simple, HTTP-based approach to real-time GraphQL subscriptions.

Why SSE?

SSE is perfect for server-to-client streaming:

  • Simpler than WebSocket - Uses standard HTTP
  • Better compatibility - Works through firewalls and proxies
  • Auto-reconnection - Built into browser EventSource API
  • Lower overhead - One-way communication is more efficient

When to Use SSE vs WebSocket

FeatureSSEWebSocket
DirectionServer → ClientBi-directional
ProtocolHTTP/HTTPSws/wss
ReconnectionAutomaticManual
Firewall-friendly✅ Yes⚠️ Sometimes blocked
Use caseNotifications, feeds, monitoringChat, gaming, collaborative tools

Basic Usage

import { SSESubscription } from './zeus'; // Create SSE client const sseClient = SSESubscription('https://your-api.com/graphql', { headers: { Authorization: 'Bearer token', }, }); // Create a subscription stream const stream = sseClient('subscription')({ messageAdded: { id: true, content: true, author: { name: true, avatar: true, }, timestamp: true, }, }); // Handle incoming data stream.on((data) => { console.log('New message:', data.messageAdded); }); // Handle errors stream.error((err) => { console.error('Stream error:', err); }); // Handle connection open stream.open(() => { console.log('SSE connection established'); }); // Handle connection close stream.off(() => { console.log('SSE connection closed'); }); // Close when done setTimeout(() => { stream.close(); }, 60000);

API Methods

on(callback)

Handle incoming data events:

stream.on((data) => { // data is fully typed based on your selection console.log(data); });

error(callback)

Handle error events:

stream.error((error) => { console.error('Error occurred:', error); // Implement retry logic or user notification });

open(callback?)

Handle connection open and optionally start the stream:

// Just handle open event stream.open(() => { console.log('Connected!'); }); // Or call without callback to start stream.open();

off(callback)

Handle connection close events:

stream.off((event) => { console.log('Connection closed:', event); });

close()

Manually close the SSE connection:

stream.close();

React Integration

Basic Hook

import { SSESubscription } from './zeus'; import { useEffect, useState } from 'react'; function useSSESubscription() { const [data, setData] = useState(null); const [isConnected, setIsConnected] = useState(false); const [error, setError] = useState(null); useEffect(() => { const sseClient = SSESubscription('https://api.example.com/graphql'); const stream = sseClient('subscription')({ liveMetrics: { timestamp: true, activeUsers: true, requestsPerSecond: true, }, }); stream.on((newData) => { setData(newData.liveMetrics); setError(null); }); stream.error((err) => { setError(err); setIsConnected(false); }); stream.open(() => { setIsConnected(true); }); stream.off(() => { setIsConnected(false); }); // Start streaming stream.open(); // Cleanup return () => { stream.close(); }; }, []); return { data, isConnected, error }; }

Live Dashboard Example

import { SSESubscription } from './zeus'; import { useEffect, useState } from 'react'; interface Metrics { userCount: number; revenue: number; errorRate: number; } function LiveDashboard() { const [metrics, setMetrics] = useState<Metrics | null>(null); const [isConnected, setIsConnected] = useState(false); useEffect(() => { const sseClient = SSESubscription('https://api.example.com/graphql', { headers: { Authorization: `Bearer ${localStorage.getItem('token')}`, }, }); const stream = sseClient('subscription')({ dashboardMetrics: { userCount: true, revenue: true, errorRate: true, }, }); stream.on((data) => { setMetrics(data.dashboardMetrics); }); stream.error((err) => { console.error('Dashboard stream error:', err); setIsConnected(false); }); stream.open(() => { console.log('Dashboard connected'); setIsConnected(true); }); stream.off(() => { setIsConnected(false); }); stream.open(); return () => { stream.close(); }; }, []); if (!metrics) { return <div>Loading...</div>; } return ( <div className="dashboard"> <h1> Live Dashboard <span className={isConnected ? 'connected' : 'disconnected'}>{isConnected ? '🟢' : '🔴'}</span> </h1> <div className="metrics"> <div className="metric"> <h2>Active Users</h2> <p>{metrics.userCount}</p> </div> <div className="metric"> <h2>Revenue</h2> <p>${metrics.revenue.toLocaleString()}</p> </div> <div className="metric"> <h2>Error Rate</h2> <p>{metrics.errorRate.toFixed(2)}%</p> </div> </div> </div> ); }

Real-Time Notifications

import { SSESubscription } from './zeus'; interface Notification { id: string; type: string; message: string; timestamp: string; } function NotificationStream() { const [notifications, setNotifications] = useState<Notification[]>([]); useEffect(() => { const sseClient = SSESubscription('https://api.example.com/graphql', { headers: { Authorization: `Bearer ${getAuthToken()}`, }, }); const stream = sseClient('subscription')({ userNotifications: { id: true, type: true, message: true, timestamp: true, }, }); stream.on((data) => { const newNotification = data.userNotifications; setNotifications((prev) => [newNotification, ...prev]); // Show browser notification if (Notification.permission === 'granted') { new Notification(newNotification.type, { body: newNotification.message, }); } }); stream.error((err) => { console.error('Notification stream error:', err); }); stream.open(); return () => { stream.close(); }; }, []); return ( <div className="notifications"> {notifications.map((notif) => ( <div key={notif.id} className="notification"> <strong>{notif.type}</strong> <p>{notif.message}</p> <span>{new Date(notif.timestamp).toLocaleString()}</span> </div> ))} </div> ); }

Error Handling & Reconnection

import { SSESubscription } from './zeus'; function createResilientStream() { let retryCount = 0; const maxRetries = 5; function connect() { const sseClient = SSESubscription('https://api.example.com/graphql'); const stream = sseClient('subscription')({ liveData: { value: true, timestamp: true, }, }); stream.on((data) => { retryCount = 0; // Reset on successful data console.log('Received:', data.liveData); }); stream.error((err) => { console.error('Stream error:', err); if (retryCount < maxRetries) { retryCount++; console.log(`Retrying (${retryCount}/${maxRetries})...`); setTimeout(() => connect(), 1000 * Math.pow(2, retryCount)); } else { console.error('Max retries reached'); } }); stream.open(); return stream; } return connect(); }

Browser Compatibility

SSE is supported in all modern browsers:

  • ✅ Chrome 6+
  • ✅ Firefox 6+
  • ✅ Safari 5+
  • ✅ Edge 79+
  • ✅ Opera 11+

Note: Internet Explorer does not support SSE natively.

Use Cases

Perfect for:

  • Live Dashboards - Real-time metrics and analytics
  • Notification Feeds - User notifications and updates
  • Activity Streams - Social media-style feeds
  • Monitoring Systems - Server health, logs
  • Stock Tickers - Financial data streams
  • Live Scores - Sports updates
  • Chat (Read-only) - Message broadcasts

Not ideal for:

  • Bi-directional Communication - Use WebSocket instead
  • Binary Data - WebSocket is better
  • Very High Frequency - WebSocket has lower overhead

Comparison with WebSocket Subscriptions

// SSE - Simpler, HTTP-based import { SSESubscription } from './zeus'; const sse = SSESubscription('https://api.com/graphql'); const stream = sse('subscription')({ data: { value: true } }); stream.on((data) => console.log(data)); stream.open(); // WebSocket - More complex, bi-directional import { Subscription } from './zeus'; const ws = Subscription('wss://api.com/graphql'); ws('subscription')({ data: { value: true } }).on((data) => { console.log(data); });

Next Steps

Explore Thunder Client →

Last updated on