Before v0.13.0, Flask projects ran their server in one terminal and a type watcher in another. Now one command does both, launching Flask’s own dev server, not an ASGI stand-in. Here is the full setup, which doubles as the walkthrough for examples/flask-nextjs.
1. The backend
# server/main.py
from flask import Flask
from flask_cors import CORS
from pyrpc_core import rpc
from pyrpc_flask import mount_flask
app = Flask(__name__)
CORS(app)
@rpc.query
def greet(name: str = "World") -> str:
return f"Hello, {name}!"
@rpc.mutation
def create_item(name: str) -> dict:
return {"name": name, "id": 1}
mount_flask(app)Nothing exotic: a normal Flask app, procedures decorated, two routes added. You can run it yourself with python main.py at any time, pyRPC never takes ownership away from you.
2. One command instead of three terminals
$ cd server && pyrpc dev pyRPC setup (runs once - saved to pyrpc.json) ? Backend framework: Flask <- preselected: mount_flask( sniffed in main.py ? Backend entry point (module[:app] - the file that calls mount_flask): main ? Client project root: ../client <- directory autocomplete, Tab accepts ? Frontend framework: Next.js <- detected from next.config.ts ✓ pyrpc.json created ✓ types generated (2 procs) → ../client/__pyrpc.ts pyRPC dev http://127.0.0.1:8000/rpc
What actually runs is Flask native:
python -m flask --app main:app run --host 127.0.0.1 --port 8000 --reload
No WSGI bridge, no uvicorn in the process tree. Tracebacks look like Flask because they are.
3. The frontend side
pyrpc dev wrote __pyrpc.ts into ../client and wired @pyrpc/types to it (tsconfig paths + a Turbopack alias). Your client file:
// client/lib/pyrpc.ts
import { createNextClient, httpBatchLink } from "@pyrpc/next"
import type { Types } from "@pyrpc/types"
export const api = createNextClient<Types>({
links: [httpBatchLink({ url: "http://localhost:8000" })],
})Then ordinary TanStack usage, api.greet.useQuery({}), api.create_item.useMutation(), fully typed against the Python signatures.
4. Things to try
- Edit
main.py, add a procedure: types regenerate within ~300ms; autocomplete updates without touching the client. - Edit
pyrpc.json: set"framework": "fastapi"just to watch the watcher terminate Flask and relaunch uvicorn. Set it back. - Start the server yourself (
python main.py) on port 8000, then runpyrpc dev: it detects the running server and only runs the type watcher.
Two terminals total: backend+types in one, npm run dev in the other. That is the whole workflow.

pyRPC