The fastapi-react example is the most direct way to understand what pyRPC does. You write Python functions, decorate them with @rpc.query or @rpc.mutation, and the React side gets fully typed useQuery / useMutation hooks, no schema file, no codegen step you have to run manually.
Prerequisites
You need Python 3.11+, Node 18+, and either uv or pip.
Project layout
fastapi-react/
server/
main.py ← FastAPI app + pyRPC procedures
pyrpc.json ← written by pyrpc dev on first run
client/
src/
pyrpc.ts ← createReactClient setup
index.tsx ← api.Provider wraps the app
App.tsx ← useQuery / useMutation callsStep 1, the server
Three procedures: two queries (read operations) and one mutation (write operation). The decorator kind is the only thing that differs, pyRPC uses it to generate the right hook type on the frontend.
# server/main.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from pyrpc_core import rpc
from pyrpc_fastapi import mount_fastapi
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@rpc.query
def read_root():
return {"Hello": "World"}
@rpc.query
def read_item(item_id: int, q: str = None):
return {"item_id": item_id, "q": q}
@rpc.mutation
def create_item(name: str, description: str = None):
return {"name": name, "description": description, "created": True}
mount_fastapi(app)Step 2, start pyrpc dev
cd server uv add pyrpc-core[fastapi] pyrpc dev
First run: a 2-question wizard writes pyrpc.json. Every run after: reads that file automatically. If you want to skip the wizard entirely:
# auto-detect module and output pyrpc dev --yes # fully explicit: CI-safe pyrpc dev --yes --module main --client ../client
pyRPC starts uvicorn on :8000, writes __pyrpc.ts in the client, and re-generates it on every .py save.
Step 3, client setup
# src/pyrpc.ts
import { createReactClient } from "@pyrpc/react"
import type { Types } from "@pyrpc/types"
export const api = createReactClient<Types>({
baseUrl: process.env.REACT_APP_API_URL ?? "http://localhost:8000",
})Wrap the root component with api.Provider, this is the TanStack Query cache boundary:
// src/index.tsx
import { api } from "./pyrpc"
root.render(
<api.Provider>
<App />
</api.Provider>
)Step 4, call the procedures
// src/App.tsx
import { useState } from "react"
import { api } from "./pyrpc"
function App() {
const [name, setName] = useState("")
const { data: greeting, isLoading } = api.read_root.useQuery()
const { data: item } = api.read_item.useQuery({ item_id: 42, q: "test" })
const createItem = api.create_item.useMutation()
return (
<div>
{isLoading ? <p>Loading…</p> : <pre>{JSON.stringify(greeting)}</pre>}
<pre>{JSON.stringify(item)}</pre>
<input value={name} onChange={e => setName(e.target.value)} />
<button
onClick={() => createItem.mutate({ name, description: `Item: ${name}` })}
disabled={createItem.isPending}
>
{createItem.isPending ? "Creating…" : "Create"}
</button>
{createItem.isSuccess && <pre>{JSON.stringify(createItem.data)}</pre>}
</div>
)
}Run it
# Terminal 1 cd server && pyrpc dev # Terminal 2 cd client && npm install && npm start
Open http://localhost:3000. The app queries all three procedures and renders the results. Rename a procedure in Python, TypeScript flags the broken call immediately.
Next steps
The same FastAPI server works with Next.js, Vue, and Svelte, only the frontend adapter changes. See the examples directory for all 12 combinations.

pyRPC