Responses and Data Streaming
To stream data to clients or manage complex JSON serialization in this project, you use specialized response classes like EventSourceResponse and utilities like jsonable_encoder.
Stream Server-Sent Events (SSE)
To stream data using the Server-Sent Events protocol, use EventSourceResponse as the response_class and yield items from your path operation.
from collections.abc import AsyncIterable
from fastapi import FastAPI
from fastapi.responses import EventSourceResponse
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
app = FastAPI()
@app.get("/items/stream", response_class=EventSourceResponse)
async def sse_items() -> AsyncIterable[Item]:
items = [
Item(name="Plumbus", description="A multi-purpose household device."),
Item(name="Portal Gun", description="A portal opening device."),
]
for item in items:
yield item
When you yield a plain object or a Pydantic model:
- FastAPI automatically serializes the item to JSON.
- It wraps the result in the SSE
data:field. - It sets the
Content-Typetotext/event-stream. - It includes proxy-friendly headers like
X-Accel-Buffering: no.
Customize SSE Events
Use the ServerSentEvent class to control metadata such as event names, IDs, and retry intervals.
from fastapi import FastAPI
from fastapi.responses import EventSourceResponse
from fastapi.sse import ServerSentEvent
app = FastAPI()
@app.get("/items/stream-sse-event", response_class=EventSourceResponse)
async def sse_items_event():
# Send a named event with an ID
yield ServerSentEvent(data={"key": "value"}, event="json-data", id="1")
# Send a comment (useful for manual keep-alive)
yield ServerSentEvent(comment="just a comment")
# Tell the client to wait 5 seconds before reconnecting
yield ServerSentEvent(data="retry-test", retry=5000)
Key Fields in ServerSentEvent
data: The payload. Can be any JSON-serializable object. Strings are JSON-encoded (quoted).event: The event type name. Browser clients can listen for this specifically usingaddEventListener(event, ...).id: The event ID. The browser sends this back in theLast-Event-IDheader upon reconnection.retry: Reconnection time in milliseconds.comment: Lines starting with:that are ignored by standard clients.
Stream Raw Data
If you need to send pre-formatted text, HTML fragments, or CSV lines without JSON encoding, use the raw_data field of ServerSentEvent.
@app.get("/items/stream-raw", response_class=EventSourceResponse)
async def sse_items_raw():
# Sent as 'data: plain text without quotes'
yield ServerSentEvent(raw_data="plain text without quotes")
# Sent as 'data: <div>html fragment</div>'
yield ServerSentEvent(raw_data="<div>html fragment</div>", event="html")
[!WARNING]
dataandraw_dataare mutually exclusive. You cannot set both on the sameServerSentEventinstance.
Manual JSON Serialization
Use jsonable_encoder to convert complex Python objects (Pydantic models, dataclasses, Enums, etc.) into JSON-compatible types like dict, list, or str. This is useful when you need to prepare data for a custom response or for storage in a database.
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
class Item(BaseModel):
name: str
item = Item(name="Plumbus")
# Returns a dict: {"name": "Plumbus"}
json_compatible_item = jsonable_encoder(item)
jsonable_encoder supports standard Pydantic parameters:
include/exclude: Set of fields to include or exclude.by_alias: Whether to use field aliases (defaults toTrue).exclude_none: Whether to exclude fields withNonevalues.
Troubleshooting
Keep-Alive Pings
FastAPI automatically sends a : ping comment every 15 seconds if the generator is idle. This prevents proxies and load balancers from timing out the connection. This interval is defined by _PING_INTERVAL in fastapi.routing.
Null Characters in IDs
The SSE specification forbids null characters (\0) in event IDs. ServerSentEvent will raise a ValueError if you attempt to use one.
Deprecated Response Classes
ORJSONResponse and UJSONResponse are deprecated in this codebase. FastAPI now serializes data directly to JSON bytes via Pydantic V2, which is more efficient than using these custom response classes. Use standard return type annotations or response_model instead.
SSE with POST
EventSourceResponse is compatible with POST requests, which is required for protocols like the Model Context Protocol (MCP).
@app.post("/items/stream-post", response_class=EventSourceResponse)
async def sse_items_post() -> AsyncIterable[Item]:
for item in items:
yield item