Introduction
Modern web applications often need real-time communication. Examples include:
- Notifications
- Chat applications
- Live dashboards
- Stock prices
- Progress tracking
- Monitoring systems
Most developers immediately think about WebSockets, but another technology is simpler and often sufficient:
Server-Sent Events (SSE).
This article explains:
- What SSE is
- How it works internally
- When to use it
- How to implement it in NestJS
- Common pitfalls
- Authentication challenges
- How to test SSE endpoints in Node.js and Vitest
What is Server-Sent Events (SSE)?
Server-Sent Events is an HTTP-based technology that allows a server to continuously push events to a client over a single long-lived HTTP connection.
Unlike a normal HTTP request:
Client ---> Request
Server ---> Response
Connection ClosedSSE keeps the connection open:
Client ---> Request
Server ---> Event 1
Server ---> Event 2
Server ---> Event 3
Connection remains openThe client passively listens for events.
SSE is One-Way Communication
SSE only allows:
Server ---> ClientIt does not support:
Client ---> ServerIf bidirectional communication is required, WebSockets are usually a better choice.
SSE vs Polling
Polling
Every 5 seconds:
Client ---> GET /notifications
Server ---> []Problems:
- Wasted requests
- Increased server load
- Delayed updates
- Poor scalability
SSE
Client ---> GET /notifications/subscribe
Server ---> Keep connection open
Server ---> Notification 1
Server ---> Notification 2
Server ---> Notification 3Advantages:
- Near real-time
- Less overhead
- Simpler than WebSockets
- Uses plain HTTP
SSE Message Format
SSE uses plain text.
Example:
data: Hello WorldJSON payload:
data: {"id":1,"title":"New Message"}Named event:
event: notification
data: {"id":1}Message with ID:
id: 123
data: {"id":1}Browser API
const es = new EventSource('/notifications/subscribe');
es.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log(data);
};SSE in NestJS
NestJS has first-class support for SSE.
Controller:
@Sse('subscribe')
sse() {
return interval(1000).pipe(
map(i => ({
data: { count: i },
})),
);
}Client receives:
Recommended by LinkedIn
data: {"count":1}
data: {"count":2}
data: {"count":3}Real Example: Notification Service
@Sse('subscribe')
sse(
@User() user: RequestUser,
) {
return this.inAppService.listen(user.id);
}Service:
listen(userId: string): Observable<MessageEvent> {
return this.subject.asObservable();
}When a notification is created:
this.subject.next({
data: notification,
});Authentication with SSE
SSE endpoints are often protected.
Example:
@UseGuards(SessionGuard)
@Sse('subscribe')This works perfectly in browsers because cookies are automatically sent.
The Testing Problem
During end-to-end testing, we encountered:
401 Unauthorizedeven though authentication worked for normal requests.
The SSE request looked like this:
new EventSource(url, {
headers: {
Cookie: cookie,
},
});The problem was:
eventsource@4does not support custom headers anymore.
The Cookie header was silently ignored.
The actual request became:
GET /notifications/in-app/subscribe
Accept: text/event-streamwithout:
Cookie: auth_session=...The server returned:
401 UnauthorizedSolution: Use Fetch Instead of EventSource
const response = await fetch(url, {
headers: {
Cookie: cookie,
Accept: 'text/event-stream',
},
});This allows cookies to be sent properly.
The response stream can then be parsed manually.
When Should You Use SSE?
SSE is excellent for:
- Notifications
- Progress tracking
- Monitoring dashboards
- Stock prices
- Live metrics
- Log streaming
When Should You Use WebSockets Instead?
Use WebSockets when:
- Bidirectional communication is required
- Chat applications
- Multiplayer games
- Collaborative editing
- Frequent client-to-server messages
SSE Advantages
- Simple HTTP protocol
- Automatic reconnection
- Lightweight
- Firewall friendly
- Easy to scale
- Native browser support
SSE Limitations
- One-way communication
- Text-only protocol
- Browser connection limits
- Less flexible than WebSockets
Final Thoughts
Server-Sent Events are one of the most underused features of modern web development. For many real-time applications, especially notifications and dashboards, SSE provides a simpler, more scalable, and easier-to-maintain alternative to WebSockets.
Understanding the protocol, its authentication model, and its testing challenges can save hours of debugging and lead to significantly cleaner real-time architectures.