Skip to main content

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 boto3 post_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_connectasync def(connection_id: str) -> None — called on $connect
  • @ws.on_disconnectasync def(connection_id: str) -> None — called on $disconnect
  • @ws.on_messageasync 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


Star History Chart

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mangum_ws-0.1.1.tar.gz (61.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mangum_ws-0.1.1-py3-none-any.whl (9.4 kB view details)

Uploaded Python 3

File details

Details for the file mangum_ws-0.1.1.tar.gz.

File metadata

  • Download URL: mangum_ws-0.1.1.tar.gz
  • Upload date:
  • Size: 61.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.11 {"installer":{"name":"uv","version":"0.10.11","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

Hashes for mangum_ws-0.1.1.tar.gz
Algorithm Hash digest
SHA256 405ff105f37329afc0d5569096981734df3b75483edd2011a4344737e767fe95
MD5 fa97b7f1e7fda6d6db0091cc26d8f6be
BLAKE2b-256 cb639c4e3d3039573c0d273121568fffbdba04e39666f39ebfc5b14b24d37c62

See more details on using hashes here.

File details

Details for the file mangum_ws-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: mangum_ws-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 9.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.11 {"installer":{"name":"uv","version":"0.10.11","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

Hashes for mangum_ws-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 a845923cf16ff3b891b7a4b56fa3cfa58a807548d913ca1599302b7e8b268ff0
MD5 97b050248c7f656b7fc7b51bd262ccd0
BLAKE2b-256 168b37dcac395afe4003da06ba7cd44f3e4a9adab630fbdfdbc2cdd7a1b5a623

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page