pyRPC v0.2.0 fixes three ship-blocking issues that eroded the “tRPC for Python” promise. Here’s what changed and why.
1. RPCCallable now actually awaits
RPCCallable.__await__ was a no-op - pass. Calling await client.add(1, 2) silently returned nothing.
The fix: RPCCallable.__call__ now detects whether a running event loop exists. In async contexts, it returns a coroutine via call_async. In sync contexts, it calls call_sync directly.
client = RPCClient("http://localhost:8000")
# Sync - works as before
result = client.add(1, 2)
# Async - now actually works!
async_result = await client.add(1, 2)The explicit .aio() method also remains available for clarity.
2. Generated types that actually mean something
Every parameter and return in the generated TypeScript interface was any:
// Before: zero type safety add(a: any, b: any): Promise<any>;
The codegen now maps Python types to TypeScript types:
| Python | TypeScript |
|---|---|
int / float | number |
str | string |
bool | boolean |
Optional[str] | string | null |
List[int] | number[] |
Dict[str, int] | Record<string, number> |
// After: real types add(a: number, b: number): Promise<number>; greet(name: string): Promise<string>;
Custom Pydantic models resolve to their class name for future model generation.
3. @pyrpc/types - install, set up, done
@pyrpc/types is now a dependency of @pyrpc/client. The package ships a placeholder src/index.ts that is replaced when codegen runs. The import path import type { Types } from "@pyrpc/types" always works - before codegen, it resolves to an empty Record<string, never>; after codegen, it resolves to your actual procedure types.
Setup is an npm install
Installing @pyrpc/client triggers a postinstall script in @pyrpc/types:
npm install @pyrpc/client
If you’re in an interactive terminal, it prompts for your backend URL, fetches the schema, and generates types - all in one step. The generated file lives inside node_modules/@pyrpc/types/src/index.ts, so import type { Types } from "@pyrpc/types" resolves immediately.
For CI or non-interactive environments, set PYRPC_URL:
PYRPC_URL=https://api.example.com npm install @pyrpc/client
No separate pyrpc init command needed. No polling. No manual steps. The postinstall handles the common case.
Types at compile time
The generated file exports Types, a TypeScript interface with your procedure signatures. Since it’s inside node_modules/@pyrpc/types, the import path is clean and standard.
The baseUrl in createClient() is the same URL used during codegen - there’s one URL, it’s your backend. No staging-vs-production confusion.
Production / CI workflow
In CI, types are generated as a build step:
# Install deps npm install @pyrpc/client # Build (CI provides PYRPC_URL, or codegen runs explicitly) pyrpc codegen https://api.example.com npm run build
The generated file can also be committed to the repo so CI doesn’t need access to a running Python server.
What’s next
- Pydantic model → TypeScript interface generation
- Dev-mode file watcher:
pyrpc devstarts a file watcher (not HTTP poller) that regenerates types when Python sources change, using the same OS-level notification APIs as uvicorn--reload
Check the quickstart to try it out.

pyRPC