python
55 lines · 9 steps
Server-Sent Events streaming in Flask
A Flask blueprint streams live events to clients over a long-lived HTTP connection using the SSE wire format.
Explained by
highlit
1import json
2import time
3import queue
4
5from flask import Blueprint, Response, request, stream_with_context
6
7from .services import broker
8
9sse = Blueprint("sse", __name__)
10
11
12def _format_event(data, *, event=None, event_id=None, retry=None):
13 lines = []
14 if event_id is not None:
15 lines.append(f"id: {event_id}")
16 if event is not None:
17 lines.append(f"event: {event}")
18 if retry is not None:
19 lines.append(f"retry: {retry}")
20 payload = json.dumps(data, separators=(",", ":"))
21 for line in payload.splitlines():
22 lines.append(f"data: {line}")
23 return "\n".join(lines) + "\n\n"
24
25
26@sse.route("/streams/<channel>")
27def stream(channel):
28 subscriber = broker.subscribe(channel)
29
30 @stream_with_context
31 def event_stream():
32 yield _format_event({"channel": channel}, event="open", retry=3000)
33 last_ping = time.monotonic()
34 try:
35 while True:
36 try:
37 message = subscriber.get(timeout=1.0)
38 except queue.Empty:
39 if time.monotonic() - last_ping >= 15:
40 yield ": keep-alive\n\n"
41 last_ping = time.monotonic()
42 continue
43 yield _format_event(
44 message["data"],
45 event=message.get("type", "message"),
46 event_id=message["id"],
47 )
48 finally:
49 broker.unsubscribe(channel, subscriber)
50
51 response = Response(event_stream(), mimetype="text/event-stream")
52 response.headers["Cache-Control"] = "no-cache"
53 response.headers["X-Accel-Buffering"] = "no"
54 response.headers["Connection"] = "keep-alive"
55 return response
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1SSE is just a plain-text format over a long-lived HTTP response — id, event, and data fields separated by newlines.
- 2Streaming responses in Flask are generators wrapped with stream_with_context so request state survives across yields.
- 3A finally block on the generator is the reliable place to release per-connection resources when a client disconnects.
Related explainers
python
import logging import uuid from contextvars import ContextVar
Request ID tracing in FastAPI middleware
middleware
context-variables
request-tracing
Intermediate
7 steps
javascript
const express = require('express'); const EventEmitter = require('events'); const router = express.Router();
Server-Sent Events with Express
server-sent-events
streaming
event-emitter
Advanced
8 steps
java
public List<OrderSummary> streamRecentOrders(LocalDateTime since, Consumer<OrderSummary> handler) { String sql = """ SELECT id, customer_id, total_cents, status, created_at FROM orders
Streaming large JDBC result sets safely
jdbc
streaming
resource-management
Intermediate
7 steps
python
from flask import Blueprint, request, jsonify from marshmallow import Schema, fields, validate, ValidationError, EXCLUDE from .models import db, User
How a Flask blueprint validates and creates users
validation
rest-api
schema
Intermediate
8 steps
python
import random import click from faker import Faker
Building a Flask seed command with Click
cli
database seeding
orm
Intermediate
7 steps
python
import smtplib from email.message import EmailMessage from threading import Thread
Sending welcome emails off the request thread in Flask
background-threads
app-context
email
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/server-sent-events-streaming-in-flask-explained-python-cbf5/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.