Advanced FastAPI Patterns • Lesson 3

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

FeatureHTTPWebSocket
ConnectionShort-livedPersistent
DirectionClient initiatesBidirectional
OverheadHeaders on every requestMinimal after handshake
Use caseRequest/responseReal-time streaming

The WebSocket Handshake

A WebSocket connection starts as a regular HTTP request with an Upgrade header:

  1. Client sends HTTP request with Upgrade: websocket
  2. Server responds with 101 Switching Protocols
  3. Connection is upgraded to WebSocket
  4. 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 endpoint
  • websocket.accept(): Completes the handshake, must be called first
  • websocket.receive_text(): Waits for and returns a text message from the client
  • websocket.send_text(msg): Sends a text message to the client
  • websocket.receive_json(): Receives and parses a JSON message
  • websocket.send_json(data): Serializes and sends a JSON message
  • websocket.close(): Closes the connection from the server side
  • WebSocketDisconnect: Exception raised when the client disconnects

Best Practices

  1. Always call accept() first before sending or receiving messages
  2. Always handle WebSocketDisconnect to avoid unhandled exceptions when clients leave
  3. Use a while True loop for continuous message handling until disconnect
  4. Validate early -- check authentication or permissions before accepting the connection
  5. Keep message handling lightweight -- offload heavy processing to background tasks
  6. Use JSON messages for structured data exchange between client and server
  7. 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