⚡ FASTAPI Websocket RPC
RPC over Websockets made easy, robust, and production ready
A fast and durable bidirectional JSON RPC channel over Websockets.
The easiest way to create a live async channel between two nodes via Python (or other clients).
-
Both server and clients can easily expose Python methods that can be called by the other side.
Method return values are sent back as RPC responses, which the other side can wait on.
-
Remote methods are easily called via the .other.method()
wrapper
-
Connections are kept alive with a configurable retry mechanism (using Tenacity)
-
As seen at PyCon IL 2021 and EuroPython 2021
Supports and tested on Python >= 3.7
Installation 🛠️
pip install fastapi_websocket_rpc
RPC call example:
Say the server exposes an "add" method, e.g. :
class RpcCalculator(RpcMethodsBase):
async def add(self, a, b):
return a + b
Calling it is as easy as calling the method under the client's "other" property:
response = await client.other.add(a=1,b=2)
print(response.result)
getting the response with the return value.
Usage example:
Server:
import uvicorn
from fastapi import FastAPI
from fastapi_websocket_rpc import RpcMethodsBase, WebsocketRPCEndpoint
class ConcatServer(RpcMethodsBase):
async def concat(self, a="", b=""):
return a + b
app = FastAPI()
endpoint = WebsocketRPCEndpoint(ConcatServer())
endpoint.register_route(app, "/ws")
uvicorn.run(app, host="0.0.0.0", port=9000)
Client
import asyncio
from fastapi_websocket_rpc import RpcMethodsBase, WebSocketRpcClient
async def run_client(uri):
async with WebSocketRpcClient(uri, RpcMethodsBase()) as client:
response = await client.other.concat(a="hello", b=" world")
print(response.result)
asyncio.get_event_loop().run_until_complete(
run_client("ws://localhost:9000/ws")
)
See the examples and tests folders for more server and client examples
Server calling client example:
- Clients can call
client.other.method()
- which is a shortcut for
channel.other.method()
- Servers also get the channel object and can call remote methods via
channel.other.method()
- See the bidirectional call example for calling client from server and server events (e.g.
on_connect
).
What can I do with this?
Websockets are ideal to create bi-directional realtime connections over the web.
- Push updates
- Remote control mechanism
- Pub / Sub (see fastapi_websocket_pubsub)
- Trigger events (see "tests/trigger_flow_test.py")
- Node negotiations (see "tests/advanced_rpc_test.py :: test_recursive_rpc_calls")
Concepts
Logging
fastapi-websocket-rpc provides a helper logging module to control how it produces logs for you.
See fastapi_websocket_rpc/logger.py.
Use logging_config.set_mode
or the 'WS_RPC_LOGGING' environment variable to choose the logging method you prefer or override completely via default logging config.
example:
from fastapi_websocket_rpc.logger import logging_config, LoggingModes
logging_config.set_mode(LoggingModes.UVICORN)
HTTP(S) Proxy
By default, fastapi-websocket-rpc uses websockets module as websocket client handler. This does not support HTTP(S) Proxy, see https://github.com/python-websockets/websockets/issues/364 . If the ability to use a proxy is important to, another websocket client implementation can be used, e.g. websocket-client (https://websocket-client.readthedocs.io). Here is how to use it. Installation:
pip install websocket-client
Then use websocket_client_handler_cls parameter:
import asyncio
from fastapi_websocket_rpc import RpcMethodsBase, WebSocketRpcClient, ProxyEnabledWebSocketClientHandler
async def run_client(uri):
async with WebSocketRpcClient(uri, RpcMethodsBase(), websocket_client_handler_cls = ProxyEnabledWebSocketClientHandler) as client:
Just set standard environment variables (lowercase and uppercase works): http_proxy, https_proxy, and no_proxy before running python script.
Pull requests - welcome!
- Please include tests for new features