WebSocket Endpoint Handling, Connection Managers, and Broadcast Loops
- Last updated
- Prerequisites:
- WebSocket protocol lifecycle
- FastAPI dependency injection
- fastapi
- websockets
- connection-manager
- real-time
- broadcast
- chat
Read at your depth
The practical view
FastAPI provides native WebSocket support: `@app.websocket('/ws/{client_id}') async def ws_endpoint(websocket: WebSocket, client_id: str): await websocket.accept(); ...`. You read with `await websocket.receive_text()` and send with `await websocket.send_text()`. A `ConnectionManager` class tracks active `WebSocket` instances in a list/set and provides `broadcast(message)` to send data to all connected clients.
The same idea in other frameworks
`@app.websocket('/ws')` with `await websocket.accept()`.`@WebSocketGateway()` with `@SubscribeMessage()`.Spring WebSocket `@MessageMapping` with STOMP broker.Node.js `ws` package (`wss.on('connection', ws => ...)`).Legacy vs modern
Unmanaged Single WebSocket vs Production ConnectionManager
Raw WebSockets without cleanup cause memory leaks on disconnect; a ConnectionManager handles lifecycle and broadcasts.
while True: await ws.receive_text() // ❌ No disconnect handling, leaks on tab closeclass ConnectionManager: ... try: ... except WebSocketDisconnect: manager.disconnect(ws) // ✅ NEWInterview gotchas
Context
Interviewers test your knowledge of dependency injection across different ASGI protocol scopes.
The mechanical answer
Yes. FastAPI's `Depends()` works seamlessly in WebSocket endpoints (e.g. for extracting query parameters, cookies, or validating JWT tokens from query strings: `async def ws(websocket: WebSocket, token: str = Depends(get_token))`). If a dependency raises an `HTTPException`, FastAPI automatically closes the WebSocket with an appropriate close code.
Trap
HTTP Authorization headers cannot be passed in standard browser JavaScript `new WebSocket(url)` connections; pass auth tokens via cookies or URL query parameters instead.
Context
Checks robust error handling in real-time server loops.
The mechanical answer
Attempting to `send_text()` to a client that disconnected without triggering a clean close will raise a `RuntimeError: Unexpected ASGI message 'websocket.send'`. Always wrap `connection.send_text()` in a `try...except` block or clean dead connections during broadcast passes.
Trap
A single failed `send_text()` inside a naive `for connection in self.active_connections:` loop will crash the broadcast loop for all other connected users.