FastAPI WebSocket Patterns — Core Concepts
Why WebSockets in FastAPI
HTTP is request-response: the client asks, the server answers, the connection closes. For real-time features — chat, notifications, live dashboards, collaborative editing — you need the server to push data to clients without being asked. WebSockets provide a persistent, bidirectional connection over a single TCP socket.
FastAPI supports WebSockets natively through Starlette. You define a WebSocket endpoint, accept the connection, and then enter a loop where you send and receive messages.
The connection lifecycle
A WebSocket connection goes through four phases:
- Handshake: The client sends an HTTP upgrade request. The server accepts (or rejects) it.
- Open: Both sides can send messages freely. Messages can be text (strings) or binary (bytes).
- Closing: Either side initiates a close. A close frame is sent, the other side acknowledges.
- Closed: The TCP connection is torn down.
In FastAPI, you handle this with await websocket.accept() to start, a while True loop for message exchange, and WebSocketDisconnect exception handling for cleanup.
Broadcasting: the connection manager pattern
The most common pattern for multi-client WebSockets is a connection manager — a class that tracks active connections and broadcasts messages to all of them:
The manager maintains a list of active WebSocket connections. When a client connects, it’s added to the list. When a message arrives, the manager iterates through all connections and sends the message to each one. When a client disconnects, it’s removed from the list.
This works for a single server process. For multiple processes or servers, you need a message broker (Redis pub/sub is the standard choice) to relay messages between processes.
Room-based messaging
Many real-time features need rooms or channels — a chat app has different rooms, a dashboard has different data feeds. The pattern extends the connection manager with a dictionary mapping room names to lists of connections.
Clients specify which room they want to join. Messages sent to a room go only to connections in that room. A client can be in multiple rooms simultaneously.
Authentication
WebSocket connections don’t have the same middleware pipeline as HTTP routes. You have two options:
Query parameter tokens: The client passes a JWT or session token as a query parameter in the WebSocket URL. The server validates it during the handshake phase before calling accept().
Cookie-based: If your app uses cookie authentication, the browser automatically sends cookies with the WebSocket upgrade request. The server reads them from the handshake headers.
Header-based authentication (like Authorization: Bearer) doesn’t work reliably because browsers don’t let JavaScript set custom headers on WebSocket connections.
Error handling and reconnection
WebSocket connections are fragile. Networks drop, clients go to sleep, servers restart. Your application needs to handle:
- Unexpected disconnections: Wrap your message loop in a try/except for
WebSocketDisconnect. Clean up the connection from your manager. - Client-side reconnection: The server can’t force a reconnection. Client code should implement exponential backoff reconnection logic.
- Heartbeats: Send periodic ping messages to detect dead connections. If a client doesn’t respond to a ping within a timeout, close the connection proactively.
WebSockets vs Server-Sent Events (SSE)
Both enable server-to-client push. The differences:
WebSockets: Bidirectional. Client and server both send messages. More complex. Requires special server support. Doesn’t work through some corporate proxies.
SSE: Server-to-client only. Simpler. Works over regular HTTP. Automatically reconnects. Works through all proxies. Client-to-server communication uses regular HTTP requests.
For notifications, live feeds, and dashboards where the client mostly listens, SSE is often simpler and more reliable. For chat, gaming, and collaborative editing where both sides talk frequently, WebSockets are the right choice.
Common misconception
Developers often think WebSockets are faster than HTTP for all real-time use cases. For scenarios where the client only needs updates (not sending frequent messages), HTTP long-polling or SSE can be equally fast with less complexity. WebSockets shine when bidirectional, low-latency communication is genuinely needed — not just for any feature that updates in real time.
The one thing to remember: FastAPI’s WebSocket support gives you persistent bidirectional connections for real-time features — use a connection manager for broadcasting, authenticate during the handshake, and consider SSE for simpler server-to-client scenarios.
See Also
- Python Aiohttp Client Understand Aiohttp Client through a practical analogy so your Python decisions become faster and clearer.
- Python Api Client Design Why building your own API client in Python is like creating a TV remote that only has the buttons you actually need.
- Python Api Documentation Swagger Swagger turns your Python API into an interactive playground where anyone can click buttons to try it out — no coding required.
- Python Api Mocking Responses Why testing with fake API responses is like rehearsing a play with stand-ins before the real actors show up.
- Python Api Pagination Clients Why APIs send data in pages, and how Python handles it — like reading a book one chapter at a time instead of swallowing the whole thing.