Python Fastapi Websockets Real-Time App

Written by

in

Imagine a web application that instantly pushes updates to every connected user without the need to refresh the page. Whether it’s a live chat, collaborative dashboard, or a real‑time gaming lobby, Python FastAPI combined with WebSockets makes this vision effortless and scalable. In this guide we’ll walk through the core concepts, set up a development environment, and build a fully functional real‑time app step by step—so you can start delivering instant, bidirectional communication to your users today.

Why FastAPI Is a Perfect Fit for Real‑Time Applications

Lightning‑fast performance

FastAPI is built on Starlette and leverages uvicorn as an asynchronous server. This stack handles thousands of concurrent connections with minimal overhead, a crucial requirement for WebSocket‑driven apps.

Native async support

All route handlers can be declared with async def, allowing you to await I/O operations (database calls, external APIs, etc.) without blocking the event loop. This means your WebSocket connections stay responsive even under heavy load.

Automatic documentation

FastAPI generates OpenAPI and ReDoc documentation out of the box. While WebSockets aren’t directly listed in the Swagger UI, you can still document your endpoints and share clear usage examples with developers.

Understanding WebSockets: The Backbone of Real‑Time Communication

WebSocket vs. traditional HTTP

  • Full‑duplex channel: Once the handshake completes, both client and server can send messages at any time.
  • Reduced latency: No need for repeated HTTP request/response cycles; data travels instantly.
  • Persistent connection: Keeps the TCP socket open, eliminating the overhead of establishing new connections.

When to use WebSockets

  1. Live chat or messaging platforms.
  2. Real‑time dashboards (stock tickers, IoT telemetry).
  3. Collaborative editing tools.
  4. Online multiplayer games.

Setting Up Your Development Environment

Before writing a single line of code, make sure you have the following tools installed:

  • Python 3.9+ (recommended 3.11 for best performance)
  • pip or poetry for dependency management
  • A modern browser with WebSocket support (Chrome, Firefox, Edge, Safari)

Run these commands to create a clean virtual environment and install FastAPI with Uvicorn:

python -m venv venv
source venv/bin/activate  # On Windows use: venv\Scripts\activate
pip install fastapi[all] uvicorn

Optional but recommended: install python-multipart for handling file uploads and pydantic for data validation (both are already included with fastapi[all]).

Building a Simple Real‑Time Chat Application

Project structure

my_chat_app/
│
├─ app/
│   ├─ __init__.py
│   ├─ main.py
│   ├─ models.py
│   └─ routers/
│       └─ ws.py
│
└─ static/
    └─ index.html

Defining a basic message model

Using pydantic ensures that every message follows a strict schema.

# app/models.py
from pydantic import BaseModel

class ChatMessage(BaseModel):
    username: str
    content: str

Creating the WebSocket router

The router handles connection lifecycle events, broadcasts messages, and cleans up closed sockets.

# app/routers/ws.py
from fastapi import APIRouter, WebSocket, WebSocketDisconnect
from typing import List

router = APIRouter()
connected_clients: List[WebSocket] = []

@router.websocket("/ws/chat")
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()
    connected_clients.append(websocket)
    try:
        while True:
            data = await websocket.receive_text()
            # Broadcast to every connected client
            for client in connected_clients:
                if client != websocket:
                    await client.send_text(data)
    except WebSocketDisconnect:
        connected_clients.remove(websocket)

Integrating the router into the FastAPI app

# app/main.py
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from .routers.ws import router as ws_router

app = FastAPI(title="FastAPI WebSocket Real‑Time Chat")

# Serve the static HTML client
app.mount("/", StaticFiles(directory="static", html=True), name="static")

# Include the WebSocket router
app.include_router(ws_router)

Client‑side HTML & JavaScript

Place the following file in static/index.html. It connects to the WebSocket endpoint, displays incoming messages, and sends new ones.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>FastAPI Real‑Time Chat</title>
    <style>
        body {font-family: Arial, sans-serif; margin: 2rem;}
        #messages {border: 1px solid #ccc; padding: 1rem; height: 300px; overflow-y: scroll;}
        #input {margin-top: 1rem;}
    </style>
</head>
<body>
    <h2>FastAPI WebSocket Chat</h2>
    <div id="messages"></div>
    <div id="input">
        <input type="text" id="username" placeholder="Your name">
        <input type="text" id="msg" placeholder="Type a message">
        <button id="send">Send</button>
    </div>

    <script>
        const ws = new WebSocket(`ws://${location.host}/ws/chat`);
        const messagesDiv = document.getElementById('messages');
        const sendBtn = document.getElementById('send');

        ws.onmessage = (event) => {
            const p = document.createElement('p');
            p.textContent = event.data;
            messagesDiv.appendChild(p);
            messagesDiv.scrollTop = messagesDiv.scrollHeight;
        };

        sendBtn.onclick = () => {
            const user = document.getElementById('username').value.trim() || 'Anonymous';
            const text = document.getElementById('msg').value.trim();
            if (!text) return;
            const payload = `${user}: ${text}`;
            ws.send(payload);
            document.getElementById('msg').value = '';
        };
    </script>
</body>
</html>

Running the application

Start the server with Uvicorn and watch the magic happen:

uvicorn app.main:app --reload

Open http://127.0.0.1:8000 in multiple browser tabs, type a username and a message, and see the text appear instantly across all tabs.

Deploying and Scaling Your FastAPI WebSocket Service

Choosing a production‑ready ASGI server

  • Uvicorn with --workers for simple horizontal scaling.
  • Gunicorn + uvicorn.workers.UvicornWorker for process management.
  • Hypercorn or Daphne if you need alternative event loops.

Load balancing WebSocket connections

Because WebSockets maintain a persistent TCP connection, traditional round‑robin load balancers can break the session if they don’t support “sticky” connections. Consider the following options:

  1. Use a reverse proxy that supports WebSocket upgrades (NGINX, Caddy, Traefik).
  2. Enable proxy_set_header Upgrade $http_upgrade; and proxy_set_header Connection "upgrade"; in NGINX.
  3. Configure sticky sessions or session affinity to keep a client on the same backend worker.

Persisting state across workers

If your app needs to share data (e.g., a list of online users), you’ll want a central store such as Redis Pub/Sub or a message

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *