fastapi-rpckit
Define your realtime API once in Python. Get the server, the contract, and typed clients for Python and TypeScript — none of which can drift apart.
Agents, browser automation, live dashboards and voice need more than request/response over HTTP: server-pushed events, binary streams, one long-lived connection. So the JSON-RPC envelope gets hand-written, the dispatch table grows by hand, and the frontend client is maintained separately — until the two disagree in production.
fastapi_rpckit makes the Python definition the single source of truth:
| You write | fastapi_rpckit gives you |
|---|---|
| an async function on a channel | validated dispatch, injection, concurrency, shutdown |
| a payload model | an OpenRPC contract as a build-time artifact |
| an error class | typed exceptions in every generated client |
| an async iterator | server-pushed events and binary streams |
| nothing else | Python and TypeScript clients, regenerated in CI |
fastapi-rpckit is the FastAPI-first WebSocket RPC successor to
pyrpckit, developed in the
Canon workspace. FastAPI is a required
dependency. REST and SSE stay with FastAPI/Canon and fastapi-streams.
The existing dispatch engine, OpenRPC contracts, binary/raw channels, and client
generators are retained. The old repository remains available.
Version 0.1.0 is the initial successor release; see the migration guide.
The idea
You define each operation once, on the server:
@tasks.method()
async def create(params: CreateTask, store: Inject[TaskStore]) -> Task:
return await store.create(params.title)
@tasks.event()
async def updated(store: Inject[TaskStore]) -> AsyncIterator[TaskUpdated]:
async for task in store.watch():
yield TaskUpdated(task=task)
One command turns that into an OpenRPC document and clients in both languages:
fastapi-rpckit generate --config rpcgen.toml
And your frontend gets the whole API fully typed — no schema written twice, no client kept in sync by hand, no stringly-typed method names:
const task = await client.tasks.create({ title: "Ship 0.6" }); // Task
for await (const update of client.tasks.updated()) { // TaskUpdated
render(update.task);
}
Run --check in CI and a definition that outgrew its clients fails the build
instead of shipping.
Install
uv add fastapi-rpckit
uv add "fastapi-rpckit[codegen]" # client generation
uv add "fastapi-rpckit[otel]" # OpenTelemetry integration
Python 3.12–3.14. FastAPI and Pydantic are required; Dishka, codegen, and OpenTelemetry remain optional extras.
Serve with FastAPI
from fastapi import FastAPI
from fastapi_rpckit import RpcChannel, RpcService, create_router
greetings = RpcChannel("greetings")
@greetings.method("hello")
async def hello() -> str:
return "Hello"
rpc = RpcService()
rpc.socket("/rpc", channels=(greetings,))
app = FastAPI()
app.include_router(create_router(rpc))
For feature-level mounting on an existing APIRouter, use RpcRoutes. For
Canon/Dishka composition, see the three-transport example.
Both reuse the same WebSocket runtime; no second RPC router model is introduced.
Quickstart
Channels group related operations and provide their namespace; a service mounts them on a socket. Their dispatch logic can also be called directly:
from fastapi_rpckit import Inject, RpcChannel, RpcModel, RpcService, RpcSuccess
class CreateTask(RpcModel):
title: str
class Task(RpcModel):
id: int
title: str
class TaskStore:
def __init__(self) -> None:
self._tasks: list[Task] = []
async def create(self, title: str) -> Task:
task = Task(id=len(self._tasks) + 1, title=title)
self._tasks.append(task)
return task
tasks = RpcChannel("tasks")
@tasks.method()
async def create(params: CreateTask, store: Inject[TaskStore]) -> Task:
"""Create a task."""
return await store.create(params.title)
app = RpcService(version=1)
app.socket("/rpc", channels=(tasks,))
async def test_create() -> None:
server = tasks.create_server(context=TaskStore())
response = await server.handle(
{
"jsonrpc": "2.0",
"id": 1,
"method": "tasks.create",
"params": {"title": "Ship 0.6"},
}
)
assert isinstance(response, RpcSuccess)
assert response.result == Task(id=1, title="Ship 0.6")
tasks.create is the wire name, the docstring becomes the contract summary,
and Inject[TaskStore] is resolved on the server — it never appears in the
public schema.
Every operation on a channel is implemented by the server: @tasks.method(),
@tasks.event(), @tasks.subscription(), and @tasks.stream() declare the
whole API. Clients call methods on the server; the server never calls methods
on the client. Push data to clients with events or subscriptions instead.
Documentation
Read the hardening stack and SDK migration guide for all 15 ticket PRs, changed defaults, and integration steps.
- Services and channels — methods, namespaces, parameter styles, sockets, protocol versions
- Dependency injection —
Inject[T], resolvers, scopes, Dishka - Connections and events — the
connecthook, rejecting handshakes, server-pushed events, limits - Typed errors — stable codes, typed details, generated exception classes
- Binary streams — upload, download, and bidirectional byte streams beside JSON-RPC
- Sockets — proxy native protocols such as CDP with typed protocol bindings
- Contract and clients —
rpcgen.toml, the CLI, the shape of generated clients - Transports — FastAPI, custom sockets, testing
Examples
examples/ holds standalone runnable scripts, and
examples/generated_clients contains real
generated Python and TypeScript output you can read before installing
anything.
Development
uv sync --all-groups
uv run ruff check .
uv run ruff format .
uv run pytest
Metadata
Release files for fastapi-rpckit 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| fastapi_rpckit-0.3.0.tar.gz | 521.2 kB | With provenance | Details for fastapi_rpckit-0.3.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| fastapi_rpckit-0.3.0-py3-none-any.whl | Python 3 | none | any | Details for fastapi_rpckit-0.3.0-py3-none-any.whl |
Total release size: 711.1 kB
Release files / fastapi_rpckit-0.3.0.tar.gz
| Download URL | fastapi_rpckit-0.3.0.tar.gz |
|---|---|
| Size | 521.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f130a0dc23e49ee3750c52128203db11209c11ae2fc1dfb359cd5c205d2183f9
|
|
BLAKE2b-256 checksum How to use checksums |
581bb3994919529c6a9a9b55ebbd237e96b1c6689732f2b7cef3d81eafc26b09
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logRelease files / fastapi_rpckit-0.3.0-py3-none-any.whl
| Download URL | fastapi_rpckit-0.3.0-py3-none-any.whl |
|---|---|
| Size | 189.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
274cde30dea3abc81d5dd83550039a2de0a0b59e90c07e86c7b40bd469ece32f
|
|
BLAKE2b-256 checksum How to use checksums |
d1b58f99dbccc135fd91cad70afc7444002f2dd7e1c4e86b3106b77a2dd2b64f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logSecurity for fastapi-rpckit 0.3.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for fastapi_rpckit-0.3.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.