Modern applications often need real-time communication for chat, notifications, live dashboards, delivery tracking, and collaborative tools.
Traditional HTTP follows a request-response model:
Client → Request → ServerClient ← Response ← Server
The client typically needs to send another request to retrieve new data. WebSockets provide a persistent, bidirectional connection that allows the client and server to send messages at any time.
Client ←══════════→ ServerWebSocket
This makes WebSockets useful when applications need frequent updates with low latency.
FastAPI supports WebSocket endpoints through the @app.websocket() decorator.
from fastapi import FastAPI, WebSocketapp = FastAPI()@app.websocket("/ws")async def websocket_endpoint(websocket: WebSocket):await websocket.accept()try:while True:message = await websocket.receive_text()await websocket.send_text(f"Server received: {message}")except WebSocketDisconnect:pass
Import WebSocketDisconnect from fastapi to handle client disconnections.
The main operations are:
accept(): Accept the WebSocket connection.receive_text(): Receive a text message.send_text(): Send a text message.close(): Close the connection.The loop allows the client and server to continue communicating without establishing a new connection for every message.
Real applications usually exchange structured data rather than plain text.
data = await websocket.receive_json()await websocket.send_json({"username": data["username"],"message": data["message"],})
A client might send:
{"username": "Viet","message": "Hello everyone!"}
JSON provides a predictable structure for chat messages, notifications, and dashboard updates.
In production, validate incoming messages and avoid trusting client-provided fields such as usernames or roles. Derive identity from authenticated connection context instead.
A chat application needs to manage multiple active connections and distribute messages between them.
User A ─┐User B ─┼──→ FastAPI → ConnectionManagerUser C ─┘
A connection manager can track active WebSocket connections and broadcast messages.
from fastapi import WebSocketclass ConnectionManager:def __init__(self):self.active_connections: list[WebSocket] = []async def connect(self, websocket: WebSocket):await websocket.accept()self.active_connections.append(websocket)def disconnect(self, websocket: WebSocket):if websocket in self.active_connections:self.active_connections.remove(websocket)async def broadcast(self, message: str):for connection in self.active_connections.copy():await connection.send_text(message)
Create a manager and use it in an endpoint:
from fastapi import WebSocket, WebSocketDisconnectmanager = ConnectionManager()@app.websocket("/ws")async def chat_endpoint(websocket: WebSocket):await manager.connect(websocket)try:while True:message = await websocket.receive_text()await manager.broadcast(message)except WebSocketDisconnect:manager.disconnect(websocket)
When a user sends a message, the server broadcasts it to all connected users, including the sender.
This is a minimal example. A production manager should also handle send failures, remove broken connections, authenticate users, and authorize access to individual chat rooms.
Important limitation: This connection list exists only in the current Python process. It does not automatically share connections across multiple workers or servers. For distributed applications, use a messaging layer such as Redis Pub/Sub to distribute events between processes.
React can connect to a WebSocket using the browser’s built-in WebSocket API.
const socket = new WebSocket("ws://localhost:8000/ws");socket.onmessage = (event) => {console.log(event.data);};socket.onopen = () => {socket.send("Hello from React!");};
For a React application, encapsulate connection management in a custom hook rather than creating connections during every render.
A typical lifecycle looks like this:
useEffect↓Create WebSocket↓Register event handlers↓Update React state↓Cleanup effect↓Close WebSocket
For example:
import { useEffect, useState } from "react";export function useWebSocket(url: string) {const [messages, setMessages] = useState<string[]>([]);useEffect(() => {const socket = new WebSocket(url);socket.onmessage = (event) => {setMessages((previous) => [...previous, event.data]);};return () => {socket.close();};}, [url]);return messages;}
The cleanup function closes the connection when the component unmounts or the URL changes. Production hooks should also handle connection status, errors, reconnection, and message parsing.
Use wss:// instead of ws:// when connecting securely in production over HTTPS.
WebSockets are useful for dashboards that need continuous updates, such as sensor readings, delivery locations, or application metrics.
A typical architecture looks like this:
Background Task↓Generate or Retrieve Data↓Publish Event↓WebSocket Connection↓React Dashboard
For example, a server can periodically publish sensor readings as JSON, and React can update the displayed values as messages arrive.
With HTTP polling, the client repeatedly requests updates:
GET /sensorGET /sensorGET /sensorGET /sensor
With WebSockets, the server can push new values as they become available:
Server ── data ──→ ReactServer ── data ──→ ReactServer ── data ──→ React
WebSockets reduce repeated request overhead when updates are frequent. However, for infrequent updates or simpler applications, ordinary HTTP polling or Server-Sent Events (SSE) may be easier to maintain.
Network failures and disconnected clients are normal in real-time applications. The server must remove disconnected clients, while the frontend should recover when connections are interrupted.
FastAPI raises WebSocketDisconnect when a connection closes during normal receive operations.
from fastapi import WebSocketDisconnecttry:while True:message = await websocket.receive_text()await manager.broadcast(message)except WebSocketDisconnect:manager.disconnect(websocket)
Production applications should also consider:
A reconnection strategy might use the following delays:
1s → 2s → 4s → 8s → 16s → 30s
Add a maximum delay and random jitter to prevent many clients from reconnecting simultaneously. If messages must not be lost, consider message IDs, acknowledgements, and a persistent event store; reconnecting alone does not guarantee delivery.
WebSockets are a strong choice for applications that need frequent, low-latency updates. A production implementation should combine connection management with authentication, authorization, resource limits, and a clear strategy for handling failures.