
WebSockets: Real-Time Bidirectional Communication
Move beyond Request/Response. Learn the foundations of WebSockets and how they enable instant, real-time data flow in FastAPI.
WebSockets: Real-Time Bidirectional Communication
The Standard HTTP protocol is like a Postcard: You send a request, the server sends a response, and the connection closes.
WebSockets are like a Phone Call: You open a connection once, and then both the server and the client can talk to each other at any time, instantly, without the overhead of opening new connections.
In this lesson, we learn how FastAPI handles real-time streams.
1. Why WebSockets?
If you are building a Chat App, a Live Trading Dashboard, or a Multiplayer Game, you can't wait for the user to "Refresh" the page. You need the server to "Push" data to the user the moment it arrives.
HTTP vs. WebSockets:
| Feature | HTTP | WebSockets |
|---|---|---|
| Communication | Unidirectional (Client -> Server) | Bidirectional (Both directions) |
| State | Stateless | Stateful (Persistent) |
| Overhead | High (Headers in every request) | Low (Once connection is open) |
2. The WebSocket Lifecycle in FastAPI
FastAPI makes WebSockets incredibly simple using the WebSocket class.
from fastapi import FastAPI, WebSocket
app = FastAPI()
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
# 1. Accept the connection
await websocket.accept()
# 2. Keep the connection open
while True:
# 3. Receive data from the client
data = await websocket.receive_text()
# 4. Send data back to the client
await websocket.send_text(f"Message received: {data}")
3. Handling Disconnections
In a production environment, users will close their tabs, lose internet connection, or move to a different page. Your code must handle these disconnections gracefully, or you will leak memory.
from fastapi import WebSocketDisconnect
@app.websocket("/ws/{client_id}")
async def websocket_endpoint(websocket: WebSocket, client_id: int):
await manager.connect(websocket)
try:
while True:
data = await websocket.receive_text()
await manager.broadcast(f"Client #{client_id} says: {data}")
except WebSocketDisconnect:
manager.disconnect(websocket)
await manager.broadcast(f"Client #{client_id} left the chat")
4. Broadcast vs. Targeted Messages
- Targeted: Sending a message to one specific user (e.g., a notification).
- Broadcast: Sending a message to every connected user (e.g., a global chat room).
Standard FastAPI doesn't manage multiple connections for you out of the box—you usually build a small ConnectionManager class to track who is currently online.
Visualizing the Persistent Connection
sequenceDiagram
participant C as Client (Browser)
participant S as Server (FastAPI)
C->>S: HTTP Handshake (Upgrade to WS)
S-->>C: 101 Switching Protocols
Note over C,S: WebSocket Connection Open
C->>S: "Hello!"
S->>C: "Hi there!"
Note right of S: Server can push data anytime
S->>C: "News update: Prices just went up!"
C->>S: Close Connection
Note over C,S: Connection Terminated
Summary
- Bidirectional: Both sides can send data at any time.
- Persistence: One connection stays open for the entire session.
accept(): You must explicitly accept a connection before talking.- Error Handling: Always use
try/except WebSocketDisconnect.
In the next lesson, we’ll look at Building a Connection Manager to handle chat rooms and notifications.
Exercise: The Heartbeat
In a real-time app, how does the server know if a client has "Vanished" without properly closing the connection (e.g., their battery died)? Hint: Research the concept of "Pings", "Pongs", and "Heartbeats" in WebSockets.