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 DiscoveryGET /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

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fastapi_openrpc-0.1.7.tar.gz (29.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fastapi_openrpc-0.1.7-py3-none-any.whl (34.5 kB view details)

Uploaded Python 3

File details

Details for the file fastapi_openrpc-0.1.7.tar.gz.

File metadata

  • Download URL: fastapi_openrpc-0.1.7.tar.gz
  • Upload date:
  • Size: 29.1 kB
  • Tags: Source
  • Uploaded using 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}

File hashes

Hashes for fastapi_openrpc-0.1.7.tar.gz
Algorithm Hash digest
SHA256 42c54b56a7a136f4989477c163e423984a3fa5580445e329f08461677d0d19b5
MD5 4eca07abd8413db729194ffec684f5e4
BLAKE2b-256 987b2ed421c2609e4a5c1b62832eeec61d92f4b3ac26eabcb50b5949cbf1bc69

See more details on using hashes here.

File details

Details for the file fastapi_openrpc-0.1.7-py3-none-any.whl.

File metadata

  • Download URL: fastapi_openrpc-0.1.7-py3-none-any.whl
  • Upload date:
  • Size: 34.5 kB
  • Tags: Python 3
  • Uploaded using 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}

File hashes

Hashes for fastapi_openrpc-0.1.7-py3-none-any.whl
Algorithm Hash digest
SHA256 bdfafdb551514b948cdcc97de9c386eacbc3d29d715c0d8e628b5517e1c986cf
MD5 71dedc4df954f475e629c0b79c52d217
BLAKE2b-256 d53ba8c2c4854ce7ec90798da23fa3ce7977c7a83d64869fd3b0d459487d7b54

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.7 This release

2 files

0.1.4

2 files

0.1.3

2 files

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