Skip to main content

fastapi-openrpc

PyPI version Python versions License

Declarative JSON-RPC 2.0 interface and OpenRPC 1.0 service discovery for FastAPI applications.

Features

  • Declarative Registration — @method() decorator with automatic JSON Schema inference
  • OpenRPC Service Discovery — GET /openrpc.json returns complete OpenRPC document
  • Pydantic Support — Automatic schema generation with $ref references
  • RpcContext Propagation — Secure caller context injection via FastAPI Depends()
  • HMAC Authentication — Built-in signature verification with Nonce replay protection
  • Role-Based Authorization — Method-level role declarations with composition semantics
  • Business Errors — Structured error codes with errors array in OpenRPC document

Installation

pip install fastapi-openrpc

Optional dependencies:

# Redis support (Nonce replay protection)
pip install fastapi-openrpc[redis]

# Development dependencies
pip install fastapi-openrpc[dev]

Quick Start

from fastapi import FastAPI
from fastapi_openrpc import OpenRpcRouter, RpcContext, method

app = FastAPI()

openrpc_router = OpenRpcRouter(
    title="My API",
    version="1.0.0",
    description="My JSON-RPC service",
)

@method(name="getUser")
async def get_user(ctx: RpcContext, user_id: str) -> dict:
    """Get user information by user_id."""
    return {"id": user_id, "name": "Alice", "roles": ctx.roles}

@method(name="listUsers")
async def list_users(ctx: RpcContext, limit: int = 10) -> list[dict]:
    """Return a list of users."""
    return [{"id": i, "name": f"User {i}"} for i in range(limit)]

app.include_router(openrpc_router)

After starting the server, access GET /openrpc.json to view the OpenRPC service document.

Authentication

HMAC Authentication

from fastapi_openrpc.authentication import HMACAuthentication

auth = HMACAuthentication(secret_key="your-secret-key")
openrpc_router = OpenRpcRouter(
    authentication=auth,
)

Nonce Replay Protection

auth = HMACAuthentication(
    secret_key="your-secret-key",
    enable_nonce=True,
    redis_url="redis://localhost:6379",
    nonce_ttl=3600,
    connect_timeout=2.0,
    operation_timeout=5.0,
    cleanup_timeout=2.0,
)

Header format:

{
  "roles": ["admin"],
  "nonce": "550e8400-e29b-41d4-a716-446655440000"
}

Authorization

from fastapi_openrpc import Require

@method(name="adminAction", required_roles=["admin"])
async def admin_action(ctx: RpcContext) -> dict:
    """Admin only."""
    return {"status": "done"}

@method(name="editContent", required_roles=["editor", "admin"], require=Require.ALL)
async def edit_content(ctx: RpcContext) -> dict:
    """Requires both editor and admin roles."""
    return {"status": "edited"}

Error Handling

Built-in Errors

from fastapi_openrpc import InvalidParams, MethodNotFound

@method(name="divide")
async def divide(ctx: RpcContext, a: float, b: float) -> float:
    if b == 0:
        raise InvalidParams("Division by zero")
    return a / b

Built-in error codes:

Error Code
Parse error -32700
Invalid Request -32600
Method Not Found -32601
Invalid Params -32602
Internal Error -32603
Forbidden -32000
Replay Detected -32002

Business Errors

Define custom errors by subclassing BusinessError:

from fastapi_openrpc import BusinessError

# Define error codes starting from -32100
class UserNotFoundError(BusinessError):
    code = -32101
    message = "User not found"

    def __init__(self, user_id: str):
        super().__init__(data={"user_id": user_id})

Register error classes with methods using the errors parameter:

from examples.errors import UserNotFoundError  # Your error class

@method(name="getUser", errors=[UserNotFoundError])
async def get_user(ctx: RpcContext, user_id: str) -> dict:
    user = db.get(user_id)
    if not user:
        raise UserNotFoundError(user_id=user_id)
    return user

The errors array appears in the OpenRPC document for that method, enabling SDK auto-generation of typed exceptions:

{
  "name": "getUser",
  "errors": [
    {"code": -32101, "message": "User not found"}
  ]
}

Library reserves -32000 ~ -32099, application custom errors start from -32100.

API Reference

OpenRpcRouter

from concurrent.futures import ThreadPoolExecutor

executor = ThreadPoolExecutor(max_workers=4)
openrpc_router = OpenRpcRouter(
    title="My API",
    version="1.0.0",
    description="...",
    servers=[{"url": "https://api.example.com"}],
    authentication=HMACAuthentication(secret_key="..."),  # Optional
    executor=executor,  # Optional, for sync handler thread pool
)
app.include_router(openrpc_router)

@method() Decorator

@method(
    name="myMethod",           # RPC method name
    tags=["admin"],           # OpenRPC tags
    summary="Short description",
    deprecated=False,
    required_roles=["admin"],  # Required roles
    require=Require.ALL,      # Role composition semantics
    errors=[MyError],        # Business error subclasses this method may raise
)
async def my_handler(ctx: RpcContext, arg1: str, arg2: int = 10) -> dict:
    return {"result": arg1}

Note: ctx: RpcContext is a reserved parameter name for injecting caller context.

Usage Examples

# Call RPC method
curl -X POST http://localhost:8000/ \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc": "2.0", "id": 1, "method": "getUser", "params": {"user_id": "42"}}'

# Service discovery
curl http://localhost:8000/openrpc.json

Documentation

For detailed user documentation, see docs/index.md.

Additional guides:

Contributing

Issues and Pull Requests are welcome.

License

MIT

Metadata

Release files for fastapi-openrpc 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fastapi-openrpc 0.2.0
File Size Uploaded Provenance Actions
fastapi_openrpc-0.2.0.tar.gz 30.2 kB Without provenance Details for fastapi_openrpc-0.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for fastapi-openrpc 0.2.0
File Interpreter ABI Platform Actions
fastapi_openrpc-0.2.0-py3-none-any.whl Python 3 none any Details for fastapi_openrpc-0.2.0-py3-none-any.whl

Total release size: 66.0 kB

Release files / fastapi_openrpc-0.2.0.tar.gz

Download URL fastapi_openrpc-0.2.0.tar.gz
Size 30.2 kB
Tags Source
SHA-256 checksum
How to use checksums
e4c7dccae0d614b86c5d8372f56edbb6dc48d859d3e3730cd3418e886fc02920
BLAKE2b-256 checksum
How to use checksums
f15e9d7f015913712a6dc01a18021f0b2f664e1e0146c6d1cd375d24b5ed1755
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.6 {"installer":{"name":"uv","version":"0.11.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / fastapi_openrpc-0.2.0-py3-none-any.whl

Download URL fastapi_openrpc-0.2.0-py3-none-any.whl
Size 35.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f0149b4c49ddfd0bc944b2a5fc2d6638309461e558c1612eceddb1e56d417efd
BLAKE2b-256 checksum
How to use checksums
d676cdea473e6cd986740ab0a0725ab6302864a32c6dfe59c172a288b66d8d10
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.6 {"installer":{"name":"uv","version":"0.11.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for fastapi-openrpc 0.2.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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A 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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page