SSE JSON Data — Sending Structured Data in Server-Sent Events
In this tutorial, you will learn about SSE JSON Data. We cover key concepts, practical examples, and best practices to help you master this topic.
SSE JSON data uses the data: field to transmit structured JSON payloads, allowing complex nested data, arrays, and typed values to be streamed from server to client in a parseable format.
What You'll Learn
- How to format JSON data in SSE events
- How to handle multi-line JSON in data fields
- How to parse SSE JSON data on the client
Why It Matters
Simple string messages are insufficient for real-world applications. Stock prices need symbol, price, change, and volume. Notifications need title, body, icon, and action_url. JSON provides the structure needed for rich real-time data.
Real-World Use
DodaTech's real-time threat intelligence feed sends JSON-based SSE events with nested threat data: threat ID, severity, affected systems, indicators of compromise, and recommended actions. The client parses this JSON and renders appropriate visualizations.
import json
import time
from flask import Flask, Response, stream_with_context
app = Flask(__name__)
def threat_feed():
threats = [
{
"id": "THR-001",
"type": "malware",
"severity": "critical",
"indicators": {
"ip": "203.0.113.50",
"hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"domain": "malicious.example.com"
},
"affected_systems": ["web-server-01", "db-primary"],
"recommended_action": "Block IP at firewall immediately",
"timestamp": time.time()
},
{
"id": "THR-002",
"type": "phishing",
"severity": "high",
"indicators": {
"url": "https://phishing.example.com/login",
"target": "DodaTech Users"
},
"affected_systems": ["email-gateway"],
"recommended_action": "Add URL to blocklist and alert users",
"timestamp": time.time()
}
]
for threat in threats:
yield f"event: threat-alert\n"
yield f"data: {json.dumps(threat)}\n\n"
time.sleep(5)
Multi-Line JSON
def multi_line_json():
"""Send multi-line JSON using multiple data: lines"""
data = {
"user": {
"id": 12345,
"name": "Jane Doe",
"email": "jane@example.com",
"preferences": {
"theme": "dark",
"notifications": True,
"language": "en-US"
},
"permissions": ["read", "write", "admin"],
"last_login": "2026-06-28T10:30:00Z",
"metadata": {
"account_age_days": 365,
"total_orders": 47,
"subscription_tier": "pro"
}
}
}
# Multi-line JSON for readability
json_str = json.dumps(data, indent=2)
for line in json_str.split('\n'):
yield f"data: {line}\n"
yield "\n"
Client-Side Parsing
const eventSource = new EventSource('/api/events/threats');
eventSource.addEventListener('threat-alert', (event) => {
let data;
// Handle multi-line JSON
try {
data = JSON.parse(event.data);
} catch (e) {
// Multi-line: data might be spread across multiple events
// EventSource concatenates data: lines with newlines
console.warn('Failed to parse JSON, might be multi-line');
return;
}
// Access structured data
renderThreatAlert(data);
updateDashboard(data.severity);
logThreat(data);
});
function renderThreatAlert(threat) {
const container = document.getElementById('threats');
const alert = document.createElement('div');
alert.className = `alert alert-${threat.severity}`;
alert.innerHTML = `
<h3>${threat.type.toUpperCase()}: ${threat.id}</h3>
<p>Severity: <strong>${threat.severity}</strong></p>
<p>Action: ${threat.recommended_action}</p>
<pre>${JSON.stringify(threat.indicators, null, 2)}</pre>
`;
container.prepend(alert);
}
Data Validation
def validate_and_send_event(event_type, data):
"""Validate JSON data before sending via SSE"""
if not isinstance(data, dict):
raise ValueError("SSE data must be a dictionary")
required_fields = ['id', 'type', 'timestamp']
missing = [f for f in required_fields if f not in data]
if missing:
raise ValueError(f"Missing required fields: {missing}")
max_size = 1024 * 100 # 100KB max
json_str = json.dumps(data)
if len(json_str) > max_size:
raise ValueError(f"Data exceeds max size of {max_size} bytes")
yield f"event: {event_type}\n"
yield f"data: {json_str}\n\n"
# Usage
app = Flask(__name__)
@app.route('/events/validated')
def validated_stream():
data = {
"id": "EVT-001",
"type": "user_action",
"timestamp": time.time(),
"action": "login",
"details": {"ip": "10.0.0.1", "browser": "Chrome"}
}
return Response(
stream_with_context(validate_and_send_event("validated-event", data)),
mimetype='text/event-stream'
)
Common Mistakes
1. Not Escaping Newlines in JSON
If the JSON string contains literal newlines, the SSE parser may interpret them as separate data lines. Use json.dumps() to produce proper escaped JSON.
2. Sending Non-Serializable Objects
Python datetime objects, custom classes, and other non-serializable types cause json.dumps() to fail. Convert them to strings first.
3. Sending Data That's Too Large
SSE connections have no standard size limit, but large messages (1MB+) can cause buffering issues on proxies. Keep individual events under 100KB.
4. Not Catching JSON Parse Errors on Client
Client-side JSON.parse() throws on invalid JSON. Always wrap in try-catch.
5. Inconsistent JSON Structure
If the same event type sometimes has different field structures, clients will have bugs. Define and document a schema for each event type.
Practice Questions
- How is JSON data transmitted in SSE?
- How do you handle multi-line JSON in SSE?
- What happens if JSON.parse fails on the client?
- What is the recommended maximum data size per event?
- How do you serialize complex Python objects for SSE?
Answers
- As a string in the
data:field(s). 2. Multiple consecutivedata:lines are concatenated with newlines. 3. The error must be caught with try-catch. 4. Under 100KB per event. 5. Convert to JSON-serializable types (strings, numbers, dicts, lists).
Challenge
Build a real-time data stream that sends complex nested JSON objects as SSE events: a financial data stream with nested order book data, or a social media stream with user profiles, posts, and engagement metrics.
FAQ
Mini Project
Build a real-time sports score streaming service that sends JSON-formatted SSE events with nested data: game metadata, team information, scores by period, player statistics, and play-by-play events. The client renders these as a live scoreboard with updating statistics.
What's Next
- Learn about event IDs and last-event-id for reconnection
- Explore retry and reconnection strategies
- Continue to connection state management with readyState
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro