Skip to content

WebSockets

Mangum provides support for WebSocket API events in API Gateway. The adapter class handles parsing the incoming requests and managing the ASGI cycle using a configured storage backend.

import os

from fastapi import FastAPI, WebSocket
from fastapi.responses import HTMLResponse
from mangum import Mangum

DSN_URL = os.environ["DSN_URL"]
WEBSOCKET_URL = os.environ["WEBSOCKET_URL"]
HTML = """
<!DOCTYPE html>
<html>
    <head>
        <title>Chat</title>
    </head>

    <body>
        <h1>WebSocket Chat</h1>
        <form action="" onsubmit="sendMessage(event)">
            <input type="text" id="messageText" autocomplete="off"/>
            <button>Send</button>
        </form>

        <ul id='messages'></ul>

        <script>
            var ws = new WebSocket('%s');
            ws.onmessage = function(event) {
                var messages = document.getElementById('messages')
                var message = document.createElement('li')
                var content = document.createTextNode(event.data)
                message.appendChild(content)
                messages.appendChild(message)
            };
            function sendMessage(event) {
                var input = document.getElementById('messageText')
                ws.send(input.value)
                input.value = ''
                event.preventDefault()
            }
        </script>
    </body>
</html>
""" % WEBSOCKET_URL

app = FastAPI()

@app.get("/")
async def get():
    return HTMLResponse(HTML)

@app.websocket("/")
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()
    while True:
        data = await websocket.receive_text()
        await websocket.send_text(f"Message text was: {data}")

handler = Mangum(app, dsn=DSN_URL)

Dependencies

The WebSocket implementation requires the following extra packages:

pip install httpx boto3

Configuring a storage backend

A data source is required in order to persist the WebSocket client connections stored in API Gateway*. Any data source can be used as long as it is accessible remotely to the AWS Lambda function. All supported backends require a dsn connection string argument to configure the connection between the adapter and the data source.

handler = Mangum(app, dsn="[postgresql|redis|dynamodb|s3|sqlite]://[...]")

*Read the section on (handling events in API Gateway for more information.)

Supported backends

The following backends are currently supported:

  • dynamodb
  • s3
  • postgresql
  • redis
  • sqlite (for local debugging)

DynamoDB

The DynamoDBBackend uses a DynamoDB table to store the connection details.

Usage
handler = Mangum(
    app,
    dsn="dynamodb://mytable"
)
Parameters

The DynamoDB backend dsn uses the following connection string syntax:

dynamodb://<table_name>[?region=<region-name>&endpoint_url=<url>]
  • table_name (Required)

    The name of the table in DynamoDB.

  • region_name

    The region name of the DynamoDB table.

  • endpoint_url

    The endpoint url to use in DynamoDB calls. This is useful if you are debugging locally with a package such as serverless-dynamodb-local.

Dependencies

This backend requires the following extra package:

pip install aioboto3

S3

The S3Backend uses an S3 bucket as a key-value store to store the connection details.

Usage
handler = Mangum(
    app,
    dsn="s3://my-bucket-12345"
)
Parameters

The S3 backend dsn uses the following connection string syntax:

s3://<bucket>[/key/...][?region=<region-name>&endpoint_url=<url>]
  • bucket (Required)

    The name of the bucket in S3.

  • region_name

    The region name of the S3 bucket.

  • endpoint_url

    The endpoint url to use in S3 calls. This is useful if you are debugging locally with a package such as serverless-s3-local.

Dependencies

This backend requires the following extra package:

pip install aioboto3

PostgreSQL

The PostgreSQLBackend requires psycopg2 and access to a remote PostgreSQL database.

Usage
handler = Mangum(
    app,
    dsn="postgresql://myuser:mysecret@my.host:5432/mydb"
)
Parameters

The PostgreSQL backend dsn uses the following connection string syntax:

postgresql://[user[:password]@][host][:port][,...][/dbname][?param1=value1&...]
  • host (Required)

    The network location of the PostgreSQL database

Read more about the supported uri schemes and additional parameters here.

Dependencies

This backend requires the following extra package:

pip install aiopg

Redis

The RedisBackend requires redis-py and access to a Redis server.

Usage
handler = Mangum(
    app,
    dsn="redis://:mysecret@my.host:6379/0"
)
Parameters

The Redis backend dsn uses the following connection string syntax:

redis://[[user:]password@]host[:port][/database]
  • host (Required)

    The network location of the Redis server.

Read more about the supported uri schemes and additional parameters here.

Dependencies

This backend requires the following extra package:

pip install aioredis

SQLite

The sqlite backend uses a local sqlite3 database to store connection. It is intended for local debugging.

Usage
handler = Mangum(
    app,
    dsn="sqlite://mydbfile.sqlite3"
)
Parameters

The SQLite backend uses the following connection string syntax:

sqlite://[file_path].db
  • file_path (Required)

    The file name or path to an sqlite3 database file. If one does not exist, then it will be created automatically.

State machine

The WebSocketCycle is used by the adapter to communicate message events between the application and WebSocket client connections in API Gateway using a storage backend to persist the connection scope. It is a state machine that handles the ASGI request and response cycle for each individual message sent by a client.

WebSocketCycle

class mangum.protocols.websockets.WebSocketCycle(request, message_type, connection_id, websocket, state=)

Manages the application cycle for an ASGI websocket connection.

  • websocket - A WebSocket connection handler interface for the selected WebSocketBackend subclass. Contains the ASGI connection scope and client connection identifier.
  • state - An enumerated WebSocketCycleState type that indicates the state of the ASGI connection.
  • app_queue - An asyncio queue (FIFO) containing messages to be received by the application.
run(self, app)

Calls the application with the websocket connection scope.

receive(self)

Awaited by the application to receive ASGI websocket events.

send(self, message)

Awaited by the application to send ASGI websocket events.

API Gateway events

There are three WebSocket events sent by API Gateway for a WebSocket API connection. Each event requires returning a response immediately, and the information required to create the connection scope is only available in the initial CONNECT event. Messages are only sent in MESSAGE events that occur after the initial connection is established, and they do not include the details of the initial connect event. Due to the stateless nature of AWS Lambda, a storage backend is required to persist the WebSocket connection details for the duration of a client connection.

CONNECT

A persistent connection between the client and a WebSocket API is being initiated. The adapter uses a supported WebSocket backend to store the connection id and initial request information.

MESSAGE

A connected client has sent a message. The adapter will retrieve the initial request information from the backend using the connection id to form the ASGI connection scope and run the ASGI application cycle.

DISCONNECT

The client or the server disconnects from the API. The adapter will remove the connection from the backend.

WebSocketCycleState

class mangum.protocols.websockets.WebSocketCycleState(value, names=None, *, module=None, qualname=None, type=None, start=1)

The state of the ASGI WebSocket connection.

  • CONNECTING - Initial state. The ASGI application instance will be run with the connection scope containing the websocket type.
  • HANDSHAKE - The ASGI websocket connection with the application has been established, and a websocket.connect event has been pushed to the application queue. The application will respond by accepting or rejecting the connection. If rejected, a 403 response will be returned to the client, and it will be removed from API Gateway.
  • RESPONSE - Handshake accepted by the application. Data received in the API Gateway message event will be sent to the application. A websocket.receive event will be pushed to the application queue.
  • DISCONNECTING - The ASGI connection cycle is complete and should be disconnected from the application. A websocket.disconnect event will be pushed to the queue, and a response will be returned to the client connection.
  • CLOSED - The application has sent a websocket.close message. This will either be in response to a websocket.disconnect event or occurs when a connection is rejected in response to a websocket.connect event.