Quickstart

Get a working pyRPC server and client in under 2 minutes.

1. Install

uv add pyrpc-core

uvicorn is included as a dependency of pyrpc-core - no need to install it separately. The pyrpc dev command uses it under the hood.

2. Server

Create server.py:

from pyrpc_core import rpc, model

@model
class User:
    name: str
    age: int

@rpc
def add(a: int, b: int) -> int:
    """Add two numbers."""
    return a + b

@rpc
def greet(user: User) -> str:
    """Greet a user."""
    return f"Hello, {user.name}!"

3. Start the dev server

Run pyrpc dev - on first run it walks you through setup: your backend framework and entry point, then the client project root and frontend framework. The backend framework decides which native dev server runs (uvicorn for FastAPI/ASGI, flask run for Flask, manage.py runserver for Django), so pick the one your project actually uses.

pyrpc dev

First run output:

pyRPC setup (runs once — saved to pyrpc.json)

? Backend framework:  FastAPI
? Backend entry point (module[:app] — the file that calls mount_fastapi):  server
? Client project root:  ../frontend
? Frontend framework:  Next.js

  ✓ types generated (2 procs) → ../frontend
  pyRPC dev  http://127.0.0.1:8000/rpc

The client root prompt autocompletes directories as you type (Tab accepts a suggestion). This creates a pyrpc.json config file and starts the dev server with auto-type regeneration on file changes. pyrpc dev writes __pyrpc.ts to your client project root and wires @pyrpc/types to it automatically.

To skip the wizard in CI or on repeat setups:

pyrpc dev --yes                       # sniff the framework, auto-detect module and client
pyrpc dev --yes --framework fastapi --module server --client ../frontend
pyrpc dev --yes --framework flask --module app --client ../frontend

With --yes, pyRPC sniffs your code for mount_fastapi / mount_flask / mount_django and refuses to guess when detection fails — pass --framework explicitly to be sure.

4. Client (TypeScript)

import { createClient, httpBatchLink } from "@pyrpc/client"
import type { Types } from "@pyrpc/types"

const api = createClient<Types>({
  links: [
    httpBatchLink({
      url: "http://localhost:8000",
    }),
  ],
})

const result = await api.add(10, 5)
const message = await api.greet({ name: "pyRPC", age: 1 })

console.log(result, message)

5. Client (Python)

You can also call your procedures from other Python services or scripts with zero codegen required.

from pyrpc_core import RPCClient

with RPCClient("http://localhost:8000") as client:
    # Everything is dynamic and introspected at runtime
    result = client.add(a=10, b=5)
    print(result)  # 15

Done

You now have a working pyRPC server + end-to-end typed contracts. Next: