With streaming responses becoming ubiquitous across applications today, I wanted to understand what they are and explore the various ways they can be implemented. This blog will explore the why, what, how, and when of streaming responses.
What are Streaming Responses?
Streaming responses allow a server to send data to the client incrementally, as it becomes available, rather than waiting to generate the entire response before sending. This enables faster perceived response times, supports large payloads, and enables real-time data delivery.
Why Use Streaming Responses?
Faster Time-to-First-Byte: Clients start receiving data immediately.
Efficient for Large Data: Avoids loading entire payloads into memory.
Real-Time Updates: Enables server push, live feeds, and event streams.
Lower Latency: Useful for APIs returning large files, logs, or progressive results.
Common Use Cases
Downloading large files (CSV, logs, media)
Real-time dashboards and event feeds
Server-Sent Events (SSE) and live notifications
Progressive rendering of web pages or reports
Streaming Response Examples: Flask vs Django vs FastAPI
# Flask streaming response example
from flask import Flask, Response
def generate_large_csv():
yield 'id,name\n'
for i in range(1, 10001):
yield f"{i},User {i}\n"
app = Flask(__name__)
@app.route('/download')
def download():
return Response(generate_large_csv(), mimetype='text/csv')
if __name__ == '__main__':
app.run(debug=True)
# Django streaming response example
from django.http import StreamingHttpResponse
def generate_large_csv():
yield 'id,name\n'
for i in range(1, 10001):
yield f"{i},User {i}\n"
def download(request):
response = StreamingHttpResponse(generate_large_csv(), content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="users.csv"'
return response
# FastAPI streaming response example
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import io
def generate_large_csv():
yield 'id,name\n'
for i in range(1, 10001):
yield f"{i},User {i}\n"
app = FastAPI()
@app.get("/download")
async def download():
return StreamingResponse(
generate_large_csv(),
media_type="text/csv",
headers={"Content-Disposition": "attachment; filename=users.csv"}
)
# Alternative: Async generator for I/O bound operations
@app.get("/download-async")
async def download_async():
async def generate_async_csv():
yield 'id,name\n'
for i in range(1, 10001):
yield f"{i},User {i}\n"
return StreamingResponse(
generate_async_csv(),
media_type="text/csv"
)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
WebSocket Streaming Examples
WebSockets provide full-duplex communication channels, enabling real-time bidirectional data streaming between client and server.
# Flask WebSocket with Flask-SocketIO
from flask import Flask
from flask_socketio import SocketIO, emit
import time
import threading
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app, cors_allowed_origins="*")
def background_task():
"""Background task that sends periodic updates"""
count = 0
while True:
time.sleep(1)
count += 1
socketio.emit('status_update',
{'data': f'Server time: {time.time()}, Count: {count}'},
namespace='/live')
@socketio.on('connect', namespace='/live')
def handle_connect():
print('Client connected')
emit('response', {'data': 'Connected to live stream'})
@socketio.on('request_data', namespace='/live')
def handle_request(json):
print(f'Received request: {json}')
emit('data_response', {'data': f'Response to: {json["message"]}'})
if __name__ == '__main__':
# Start background task
thread = threading.Thread(target=background_task)
thread.daemon = True
thread.start()
socketio.run(app, debug=True, port=5000)