FastAPI browser demo

For the shared RPC walkthrough with framework tabs, see the quick start.

Install the server framework and optional Python client:

pip install 'wsrpc-asgi[fastapi,client]' uvicorn

FastAPI and a browser

The complete server is in examples/main/server.py. It exposes uuid4, serves the bundled JavaScript client and a browser page. Static paths are resolved relative to the example file, so the server does not depend on the current directory.

import uuid
from pathlib import Path

from fastapi import FastAPI, WebSocket
from fastapi.staticfiles import StaticFiles
from wsrpc_asgi import STATIC_DIR, WebSocketAsync


class Socket(WebSocketAsync):
    pass


async def get_random_uuid(socket: Socket) -> str:
    return str(uuid.uuid4())


def create_app() -> FastAPI:
    Socket.add_route("uuid4", get_random_uuid)
    app = FastAPI()

    @app.websocket("/ws/")
    async def rpc(websocket: WebSocket):
        await Socket.serve(websocket)

    app.mount("/js", StaticFiles(directory=STATIC_DIR), name="js")
    app.mount("/", StaticFiles(directory=Path(__file__).parent, html=True))
    return app


app = create_app()

if __name__ == "__main__":
    import uvicorn

    uvicorn.run(app, host="127.0.0.1", port=8000)

From this documentation checkout, after installing the ASGI library and FastAPI (see the framework guide):

uv run --no-sync python asgi/examples/main/server.py

Open http://127.0.0.1:8000/web-client.html. The browser connects to /ws/ and requests a UUID. Its source is:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>WSRPC quick start</title>
  <script src="/js/wsrpc.min.js"></script>
</head>
<body>
  <label>UUID <input id="result" readonly></label>
  <button id="generate" type="button">Generate UUID</button>
  <p id="error" role="alert"></p>
  <script>
    const url = new URL("/ws/", window.location.href);
    url.protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
    const rpc = new WSRPC(url.toString());
    rpc.connect();

    document.getElementById("generate").addEventListener("click", async () => {
      document.getElementById("error").textContent = "";
      try {
        document.getElementById("result").value = await rpc.call("uuid4");
      } catch (error) {
        document.getElementById("error").textContent = error.message || String(error);
      }
    });
  </script>
</body>
</html>

Python client

In another terminal run:

uv run --no-sync python asgi/examples/main/client.py
import asyncio

from wsrpc_asgi import WSRPCClient


async def main(endpoint: str = "ws://127.0.0.1:8000/ws/") -> None:
    async with WSRPCClient(endpoint) as client:
        print(await client.proxy.uuid4())


if __name__ == "__main__":
    asyncio.run(main())

A peer can also expose callbacks. The larger server demo calls the Python client’s joke route. Run the server and client in separate terminals:

uv run --no-sync python asgi/examples/server.py
uv run --no-sync python asgi/examples/client.py

For Starlette, Django, Channels, Quart, Litestar, Falcon, Sanic, BlackSheep or a standalone endpoint see framework integrations.