WebSocket support for Mangum (AWS API Gateway WebSocket API → FastAPI)
Project description
mangum-ws
WebSocket support for Mangum — use FastAPI WebSockets on AWS Lambda behind API Gateway WebSocket API.
Try it now — a working chat demo:
git clone https://github.com/okigan/mangum-ws.git && cd mangum-ws
uv sync --group dev
uv run python examples/demochat.py
# Open http://localhost:8000 in two browser tabs
Problem
Mangum lets you run ASGI apps (like FastAPI) on AWS Lambda behind API Gateway. It handles HTTP requests well, but API Gateway WebSocket API works differently: there's no persistent server — each WebSocket event ($connect, $disconnect, messages) triggers a separate Lambda invocation, and sending messages back requires calling the API Gateway Management API via HTTP.
Mangum doesn't natively support this. mangum-ws bridges the gap with a single MangumWS object:
- Inbound (Lambda): converts API Gateway WebSocket events into FastAPI route calls via a Mangum custom handler
- Outbound (Lambda): sends messages back to clients via
boto3post_to_connection - Local dev: mounts a real FastAPI WebSocket endpoint so the same code works without AWS
Install
uv add mangum-ws
# With AWS support (boto3):
uv add "mangum-ws[aws]"
For local development with ws.mount(), uvicorn needs a WebSocket library:
uv add "uvicorn[standard]"
Quick Start
import os
from fastapi import FastAPI
from mangum import Mangum
from mangum_ws import MangumWS
app = FastAPI()
# One object, auto-selects AWS or local based on endpoint URL
ws = MangumWS(endpoint_url=os.getenv("APIGW_MANAGEMENT_URL"))
# Register WebSocket lifecycle handlers (write once, used everywhere)
@ws.on_connect
async def handle_connect(connection_id: str):
print(f"Connected: {connection_id}")
@ws.on_disconnect
async def handle_disconnect(connection_id: str):
print(f"Disconnected: {connection_id}")
@ws.on_message
async def handle_message(connection_id: str, data: dict):
# Subscribe this connection to a topic, etc.
pass
# Standard FastAPI: include the router (generates /internal/websocket/* routes)
app.include_router(ws.router)
# Local dev: mount a real WebSocket endpoint (no-op in production)
ws.mount(app)
# Sending messages back to clients
async def notify(connection_ids: set[str], payload: dict):
gone = await ws.send(connection_ids=connection_ids, data=payload)
# Remove gone connection IDs from your subscription store
return gone
# Lambda handler
handler = Mangum(app, custom_handlers=[ws.handler])
How It Works
In production, API Gateway WebSocket API manages connections and invokes Lambda for each event ($connect, $disconnect, sendmessage). The library converts these into FastAPI route calls via a Mangum custom handler, and sends messages back via boto3.
For local development, ws.mount() creates a real @app.websocket() endpoint that reuses the same handlers — so your code works identically in both environments.
API
MangumWS(endpoint_url=None)
Main entry point. Uses AwsGateway (boto3) if endpoint_url is provided, LocalGateway (in-memory) otherwise.
@ws.on_connect / @ws.on_disconnect / @ws.on_message
Decorators to register WebSocket lifecycle handlers. Each handler is written once and used in both Lambda (ws.router) and local dev (ws.mount):
@ws.on_connect—async def(connection_id: str) -> None— called on$connect@ws.on_disconnect—async def(connection_id: str) -> None— called on$disconnect@ws.on_message—async def(connection_id: str, data: dict) -> None— called on custom routes (e.g.sendmessage)
All are optional.
ws.router
A fastapi.APIRouter containing POST /internal/websocket/{connect,disconnect,sendmessage}/{connection_id} routes that dispatch to your registered handlers. Add with app.include_router(ws.router).
ws.mount(app, path="/")
Mount a real @app.websocket() endpoint for local development, reusing the registered handlers. No-op in production (so you can call it unconditionally).
ws.handler
The Mangum custom handler class. Pass to Mangum(app, custom_handlers=[ws.handler]).
ws.send(connection_ids, data) -> set[str]
Send data (dict or string) to all connection_ids. Returns the set of gone (disconnected) connection IDs.
ws.send_to_connection(connection_id, data) -> bool
Send to a single connection. Returns False if the connection is gone.
ws.is_local
True when backed by LocalGateway (local dev), False when backed by AwsGateway.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mangum_ws-0.1.2.tar.gz.
File metadata
- Download URL: mangum_ws-0.1.2.tar.gz
- Upload date:
- Size: 61.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b8d4a41438a5dcf73bb98ffec8498e862d39502b83551c083855ffd493534bc9
|
|
| MD5 |
a09cf392d40a86818aa5342ac4a109ef
|
|
| BLAKE2b-256 |
183fb11889e143f743064f6291f110b1b8f12aa24d0cf8c25bac3f919e08280d
|
File details
Details for the file mangum_ws-0.1.2-py3-none-any.whl.
File metadata
- Download URL: mangum_ws-0.1.2-py3-none-any.whl
- Upload date:
- Size: 9.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6ce3804eaf56741b3d9552661ab2e170a1291e488b1850d8ba2b89e66254ce89
|
|
| MD5 |
43ea0e5b68ae9d332d4769ebdd81005e
|
|
| BLAKE2b-256 |
15fbd70dee0b75d2c307093bf7d3f5ef8f7a6724f14ab8388b6f440330974d0d
|