How to Handle Server-Sent Events vs Axios Requests
This article explores the fundamental architectural differences between standard HTTP requests handled by Axios and real-time streaming with Server-Sent Events (SSE). It explains why standard Axios patterns are not suitable for consuming SSE in browser environments, how to properly implement native SSE listeners, and how to handle streaming data with Axios in Node.js environments when standard request-response cycles are not enough.
Standard Requests with Axios
Axios is a promise-based HTTP client designed around the traditional Request-Response cycle. In this model:
- The client opens a TCP connection.
- The client sends an HTTP request (e.g.,
GET,POST). - The server processes the request and returns a complete payload with
a closing status code (e.g.,
200 OK). - The connection is terminated or returned to a connection pool.
import axios from 'axios';
async function fetchUserData() {
try {
const response = await axios.get('/api/user/123');
console.log('Data received:', response.data);
} catch (error) {
console.error('Request failed:', error);
}
}This pattern is ideal for atomic, stateless transactions, such as retrieving a record, submitting a form, or performing CRUD operations.
What are Server-Sent Events (SSE)?
Server-Sent Events (SSE) establish a persistent, unidirectional stream from the server to the client over standard HTTP.
Unlike standard requests:
- The connection remains open indefinitely.
- The server sends data chunks formatted as
text/event-stream. - The client receives continuous, real-time updates without polling.
- The browser automatically handles reconnections if the connection drops.
Why Axios Is Not Built for Browser-Based SSE
In the browser, Axios relies on XMLHttpRequest (XHR).
XHR buffers incoming data by default and does not expose a native
event-dispatching mechanism for the text/event-stream
format.
While you can technically listen to onDownloadProgress
in Axios, it requires manual parsing of SSE frames (handling
event:, data:, id:, and
\n\n boundaries), and it does not provide automatic
reconnection.
The Correct
Approach: Native EventSource (Browser)
The standard, most efficient way to handle SSE in the browser is the
native EventSource API.
const eventSource = new EventSource('/api/notifications');
// Listen for default message events
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('New notification:', data);
};
// Listen for custom named events
eventSource.addEventListener('user_update', (event) => {
const data = JSON.parse(event.data);
console.log('User update:', data);
});
// Handle connection errors
eventSource.onerror = (error) => {
console.error('SSE connection error:', error);
// Native EventSource automatically attempts reconnection
};
// Close connection when no longer needed
function stopListening() {
eventSource.close();
}When You Need Custom Headers in the Browser
The native EventSource API does not allow custom HTTP
request headers (such as
Authorization: Bearer <token>). If headers are
required, developers typically use libraries like
@microsoft/fetch-event-source rather than Axios, as it
wraps the browser's native fetch with streaming
support.
Handling SSE with Axios in Node.js
In a Node.js runtime environment, Axios uses the
native http module instead of XMLHttpRequest.
This allows Axios to receive a readable stream via
responseType: 'stream'.
const axios = require('axios');
async function listenToStream() {
const response = await axios({
method: 'get',
url: 'https://api.example.com/stream',
responseType: 'stream',
headers: {
Accept: 'text/event-stream',
Authorization: 'Bearer YOUR_TOKEN'
}
});
const stream = response.data;
stream.on('data', (chunk) => {
const message = chunk.toString();
console.log('Stream chunk:', message);
// Parse SSE lines manually
if (message.startsWith('data:')) {
const dataContent = message.replace(/^data:\s*/, '').trim();
console.log('Parsed Event Data:', dataContent);
}
});
stream.on('end', () => {
console.log('Stream ended by server');
});
stream.on('error', (err) => {
console.error('Stream error:', err);
});
}
listenToStream();Direct Comparison
| Feature | Standard Axios Request | Server-Sent Events (SSE) |
|---|---|---|
| Communication Model | Request \(\rightarrow\) Single Response | Request \(\rightarrow\) Indefinite Stream |
| Direction | Bidirectional (half-duplex) | Unidirectional (Server \(\rightarrow\) Client) |
| Connection Lifecycle | Closes immediately after response | Stays open continuously |
| Content Type | application/json,
text/html, etc. |
text/event-stream |
| Primary Browser Tool | Axios / fetch |
EventSource /
fetch-event-source |
| Auto-Reconnection | No (requires manual retry logic) | Yes (native to
EventSource) |
| Best Use Case | CRUD operations, discrete API calls | Live notifications, real-time metrics, LLM text streaming |