Introduction to Server-Sent Events
In this tutorial, you will learn about Introduction to Server. We cover key concepts, practical examples, and best practices to help you master this topic.
Learn Server-Sent Events basics: how SSE enables server-to-client real-time push over HTTP, the EventSource API, event stream format, and when to use SSE in backend applications.
What You Learn
You will learn what Server-Sent Events are, how they differ from polling and WebSockets, the text/event-stream format, how to connect with the EventSource API, and when SSE is the right choice.
Why It Matters
Real-time updates improve user experience: live notifications, stock tickers, progress bars, feed updates. SSE provides a simpler alternative to WebSockets for one-way server-to-client streaming, using standard HTTP without special server support.
Real-World Use
DodaTech uses SSE for live deployment logs, scan progress updates, notification feeds, and system monitoring dashboards. SSE is preferred over WebSockets for these use cases because the data flows only from server to client.
Basic SSE Example
from http.server import HTTPServer, BaseHTTPRequestHandler
import time
import json
class SSEHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-Type', 'text/event-stream')
self.send_header('Cache-Control', 'no-cache')
self.send_header('Connection', 'keep-alive')
self.end_headers()
for i in range(5):
data = json.dumps({'count': i, 'message': f'Event {i}'})
self.wfile.write(f"data: {data}\n\n".encode())
time.sleep(1)
server = HTTPServer(('localhost', 8080), SSEHandler)
print("SSE server on http://localhost:8080")
server.serve_forever()
<!DOCTYPE html>
<html>
<body>
<div id="output"></div>
<script>
const source = new EventSource('http://localhost:8080');
source.onmessage = (event) => {
const data = JSON.parse(event.data);
document.getElementById('output').innerHTML +=
`<p>Count: ${data.count} - ${data.message}</p>`;
};
</script>
</body>
</html>
Expected output (in browser):
Count: 0 - Event 0
Count: 1 - Event 1
Count: 2 - Event 2
Count: 3 - Event 3
Count: 4 - Event 4
The text/event-stream Format
# SSE format: "field: value" pairs separated by newlines
# Events are separated by double newlines
# Simple data event
data: Hello World
# Event with ID
id: 42
data: {"message": "Hello"}
# Named event type
event: update
data: {"status": "completed"}
# Multi-line data
data: line 1
data: line 2
# Retry interval (milliseconds)
retry: 5000
# Comment (ignored by client)
: this is a comment
data: last message
Connecting with EventSource
// Basic connection
const source = new EventSource('/events');
// Listen for all messages
source.onmessage = function(event) {
console.log('Received:', event.data);
};
// Listen for specific event types
source.addEventListener('user-update', function(event) {
console.log('User update:', JSON.parse(event.data));
});
source.addEventListener('notification', function(event) {
showNotification(event.data);
});
// Error handling
source.onerror = function(event) {
if (event.eventPhase === EventSource.CLOSED) {
console.log('Connection closed');
} else {
console.log('Connection error, will auto-reconnect');
}
};
// Close connection
// source.close();
When to Use SSE
use_cases = {
'sse': {
'good_for': [
'Live notifications',
'Status updates',
'Progress bars',
'Feed updates',
'Log streaming',
'Metrics dashboards',
],
'one_way': 'Server -> Client only',
'simple': 'Uses standard HTTP',
'auto_reconnect': 'Built into EventSource',
},
'websocket': {
'good_for': [
'Chat applications',
'Collaborative editing',
'Gaming',
'Real-time forms',
],
'two_way': 'Bidirectional',
'complex': 'Requires WebSocket handshake',
'reconnect': 'Must implement manually',
},
'polling': {
'good_for': [
'Infrequent updates',
'Simple implementations',
'Legacy browser support',
],
'overhead': 'HTTP request per update',
'latency': 'Up to polling interval delay',
'bandwidth': 'Headers waste on each poll',
},
}
Common Mistakes
1. Missing Content-Type Header
Without Content-Type: text/event-stream, the browser treats the response as regular HTTP and buffers it. The EventSource API will not work.
2. No Cache-Control Header
Browsers and proxies may cache SSE responses. Set Cache-Control: no-cache to prevent Caching.
3. Sending Events Without Newlines
SSE requires double newlines (\n\n) between events. Single newlines are ignored as line continuations.
4. Forgetting the Connection: keep-alive Header
Without Connection: keep-alive, the HTTP connection may close after each event, defeating the purpose of SSE.
5. Using SSE for Bidirectional Communication
SSE is one-way server-to-client. For bidirectional, use WebSockets. Trying to send data to the server via SSE requires separate HTTP requests.
Practice Questions
1. What MIME type does SSE use?
text/event-stream. The server must set this Content-Type header for the EventSource API to work.
2. How are SSE events delimited?
By double newlines (\n\n). Each event is separated by a blank line between the event fields.
3. What is the main advantage of SSE over WebSockets?
SSE uses standard HTTP, auto-reconnects natively, and is simpler to implement for one-way server-to-client streaming.
4. How does the browser handle SSE connection loss?
The EventSource API automatically reconnects after a connection loss. The server can use Last-Event-ID to resume from the last event.
Challenge
Build a simple SSE server that sends stock price updates every 2 seconds. Each event includes a stock symbol, price, and timestamp. Connect a client that displays prices in real time and highlights price changes.
FAQ
Mini Project: SSE Progress Updates
import time
import json
from http.server import HTTPServer, BaseHTTPRequestHandler
class ProgressSSE(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-Type', 'text/event-stream')
self.send_header('Cache-Control', 'no-cache')
self.send_header('Connection', 'keep-alive')
self.send_header('Access-Control-Allow-Origin', '*')
self.end_headers()
total = 100
for i in range(total + 1):
event = {
'progress': i,
'total': total,
'percent': i,
'status': 'in_progress' if i < total else 'completed',
'message': f'Processing item {i}/{total}',
}
self.wfile.write(f"event: progress\ndata: {json.dumps(event)}\n\n".encode())
time.sleep(0.1)
server = HTTPServer(('localhost', 8081), ProgressSSE)
print("Progress SSE on http://localhost:8081")
server.serve_forever()
What's Next
Now that you understand SSE basics, compare SSE vs WebSocket to choose the right technology, then learn the event stream format in detail.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro