WebSocket Basics
Learn how to create WebSocket endpoints in FastAPI for real-time bidirectional communication between client and server.
🎯 What You'll Learn
- •Understand the WebSocket protocol and how it differs from HTTP
- •Create a WebSocket endpoint that accepts connections
- •Send and receive text messages over a WebSocket connection
- •Handle WebSocket disconnections gracefully
WebSocket Basics
What You'll Learn
In this lesson you will learn how to create WebSocket endpoints in FastAPI for real-time, bidirectional communication. You will understand:
- What the WebSocket protocol is and how it differs from HTTP
- How to create WebSocket endpoints in FastAPI
- The WebSocket connection lifecycle: accept, send, receive, close
- How to handle client disconnections gracefully
Theory
What are WebSockets?
HTTP follows a request-response pattern: the client sends a request, the server sends a response, and the connection is done. This works well for most web applications, but some use cases need real-time, bidirectional communication:
- Chat applications: Messages should appear instantly
- Live dashboards: Data updates should stream continuously
- Collaborative editing: Changes should sync in real time
- Gaming: Actions and state need constant exchange
WebSockets solve this by establishing a persistent, full-duplex connection between client and server. Once connected, either side can send messages at any time without waiting for the other.
HTTP vs WebSocket
| Feature | HTTP | WebSocket |
|---|---|---|
| Connection | Short-lived | Persistent |
| Direction | Client initiates | Bidirectional |
| Overhead | Headers on every request | Minimal after handshake |
| Use case | Request/response | Real-time streaming |
The WebSocket Handshake
A WebSocket connection starts as a regular HTTP request with an Upgrade header:
- Client sends HTTP request with
Upgrade: websocket - Server responds with
101 Switching Protocols - Connection is upgraded to WebSocket
- Both sides can now send messages freely
WebSockets in FastAPI
FastAPI provides first-class support for WebSockets through Starlette. Here is the basic pattern:
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
app = FastAPI()
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
try:
while True:
data = await websocket.receive_text()
await websocket.send_text(f"Echo: {data}")
except WebSocketDisconnect:
pass
Connection Lifecycle
A WebSocket connection goes through these stages:
1. Accept the connection:
await websocket.accept()
This completes the handshake and establishes the connection.
2. Send and receive messages:
# Receive text
data = await websocket.receive_text()
# Send text
await websocket.send_text("Hello!")
# You can also work with bytes and JSON
data = await websocket.receive_json()
await websocket.send_json({"key": "value"})
3. Handle disconnection:
try:
while True:
data = await websocket.receive_text()
except WebSocketDisconnect:
# Client disconnected
print("Client left")
When a client disconnects, the next receive call raises WebSocketDisconnect. Always handle this exception to avoid unhandled errors.
Path Parameters with WebSockets
Just like HTTP endpoints, WebSocket endpoints support path parameters:
@app.websocket("/ws/{user_id}")
async def websocket_endpoint(websocket: WebSocket, user_id: int):
await websocket.accept()
await websocket.send_text(f"Welcome, user {user_id}!")
Query Parameters
WebSocket endpoints also support query parameters:
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket, token: str = None):
if token != "secret":
await websocket.close(code=1008)
return
await websocket.accept()
Key Concepts
@app.websocket(path): Decorator to define a WebSocket endpointwebsocket.accept(): Completes the handshake, must be called firstwebsocket.receive_text(): Waits for and returns a text message from the clientwebsocket.send_text(msg): Sends a text message to the clientwebsocket.receive_json(): Receives and parses a JSON messagewebsocket.send_json(data): Serializes and sends a JSON messagewebsocket.close(): Closes the connection from the server sideWebSocketDisconnect: Exception raised when the client disconnects
Best Practices
- Always call
accept()first before sending or receiving messages - Always handle
WebSocketDisconnectto avoid unhandled exceptions when clients leave - Use a
while Trueloop for continuous message handling until disconnect - Validate early -- check authentication or permissions before accepting the connection
- Keep message handling lightweight -- offload heavy processing to background tasks
- Use JSON messages for structured data exchange between client and server
- Add a health check endpoint so monitoring tools can verify the server is running
Additional Resources
💡 Hint
Use '@app.websocket("/ws")' to create a WebSocket endpoint. Inside, call 'await websocket.accept()' first, then use a while loop with 'await websocket.receive_text()' and 'await websocket.send_text()'. Wrap the loop in try/except WebSocketDisconnect.
Ready to Practice?
Now that you understand the theory, let's put it into practice with hands-on coding!
Start Interactive Lesson