Blog
Design notes, deep dives, and updates from the pyrpc team.
From tRPC to pyRPC: what stays, what goes, what gets easier
Side-by-side comparison for tRPC users: same hooks, Python server, no Zod, no links chain.
Why we chose TanStack Query — and what it gives you for free
Caching, stale-while-revalidate, deduplication, and mutation invalidation without reinventing the wheel.
Testing procedure kinds: what we covered
Tests across four layers: Python decorators, introspection schema, codegen output, and adapter proxy.
Backward compatibility: how @rpc stayed working through v0.9.0
Why bare @rpc, no-kinds adapters, and existing installs all work without changes.
The CI/CD publish pipeline: tag-triggered, chained, OIDC
How publish.yml chains 6 jobs: PyPI via OIDC, npm types/client/react/adapters, and GitHub Release.
How we publish: from git tag to npm and PyPI
The full release flow: release.mjs, tag push, CI chain, OIDC for PyPI, and manual fallback.
The Svelte adapter: framework-native, zero ceremony
How createSvelteClient integrates with TanStack Svelte Query stores and Svelte-native patterns.
The Vue adapter: same contract, Vue-native patterns
How createPyrpcVue uses plugins instead of providers, composables instead of hooks.
Building the Next.js example app: file by file
Walkthrough of examples/nextjs: server.py, lib/pyrpc.ts, layout, RSC prefetch, and client hooks.
The tiny change in @pyrpc/types that powers everything
Why ProcedureKinds and procedureKinds are in @pyrpc/types, and how the const satisfies pattern works.
The npx daemon: 715x faster type generation
How a persistent Node.js process drops jsonschema-ts from 3.3s to 4.6ms.
Inside the codegen template: what client.ts.j2 generates
What the Jinja2 template produces: Types interface, ProcedureKinds, procedureKinds, and model interfaces.
Router.merge: how pyRPC handles namespaces
Split procedures into separate modules and combine them with Router.merge() for larger projects.
How procedure kinds flow from Python to TypeScript
Trace @rpc.query/@rpc.mutation from Python decorator through introspection, codegen, and into typed hooks.
Publishing guide: npm @pyrpc/* and PyPI
release.mjs, build order, GitHub Actions tag publish, and manual npm/twine steps for new adapter packages.
v0.9.0 — Framework adapters, procedure kinds, one api object
Release notes for @pyrpc/react, next, vue, svelte, server kinds, and the unified api DX.
Migrating from createClient to TanStack hooks
Keep Promise calls via api.client / createCaller while adopting useQuery — and annotate mutations on the server.
Vue and Svelte adapters: same contract, stack-native setup
createPyrpcVue with api.plugin, createSvelteClient with createQuery/createMutation, and TanStack conventions per framework.
Guide: @pyrpc/react from zero to useQuery
Minimal install, api.Provider, useQuery/useMutation, and useUtils for a Vite or CRA-style React app.
Next.js RSC: prefetch, dehydrate, and HydrationBoundary
What each server helper does, when you can skip hydration, and why prefetch is for queries not mutations.
@rpc.query and @rpc.mutation: why procedure kinds exist
How server-side query/mutation kinds flow through codegen into TanStack hooks without a second import in app code.
One api object: Provider, prefetch, and hooks in the same place
Why createNextClient / createReactClient return a single value with procedures, Provider, and server helpers — and why the variable can be named api or client.
How to version, edit, and ship pyRPC’s multi-package surface
Synchronized npm @pyrpc/* versions, PyPI independence, what to edit where, and PR/release standards for a multi-package monorepo.
Tutorial: Next.js App Router + TanStack Query with pyRPC
Step-by-step: @rpc.query/@rpc.mutation, createNextClient, RSC prefetch, HydrateClient, and client hooks.
Deep dive: framework adapters, TanStack Query, and procedure kinds
Architecture of @pyrpc/react, next, vue, and svelte — one transport, flat DX, query/mutation kinds, and Next.js hydration.
Following an RPC Call: From TypeScript Client to Python Function and Back
A step-by-step trace through pyrpc's RPC Call Flow: Proxy interception, HTTP transport, envelope validation, Router lookup, Procedure execution with TypeAdapters, and the error path.
A Visual Tour of pyrpc's Architecture
Eight diagrams, seven packages, one framework: a guided walkthrough of pyrpc's architecture from the system landscape down to individual components and dynamic flows.
Architecture as Code: Mapping pyrpc with LikeC4
How we turned pyrpc's architecture into version-controlled, interactive diagrams using LikeC4 — and why every framework should do the same.
715x faster type generation with the npx daemon
A persistent Node.js daemon replaces per-call npx subprocesses, dropping type regeneration from 3.3s to 4.6ms. How it works, the benchmarks, and the edge cases we handled.
v0.7.3 - Django adapter, FastAPI/Flask fixes
A new Django adapter (pyrpc-django-adapter) with native async views, plus an introspection crash fix that affected all adapters when no explicit router was provided.
v0.6.0 - Client distribution and package standardization
npx pyrpc sync, postinstall prompt, framework extras (pyrpc-core[fastapi], pyrpc-core[flask]), adapter auto-install, pyrpc.json config, distribution modes, and package standardization.
Server mode: type distribution across repositories
How server mode works: the schema endpoint stays in memory, pyrpc never writes to the client filesystem, and the client fetches types on demand via npx pyrpc sync.
Workspace mode: what happens when you run pyrpc dev
A step-by-step walkthrough of workspace mode: config resolution, client root validation, migration checks, file watcher loop, dev server startup, and CI compatibility.
Distribution modes: workspace and server
Two distribution modes for pyrpc: workspace (monorepo, types written directly to client) and server (separate repos, types fetched via HTTP). When to use each and how they work under the hood.
Why save_typescript_client() refuses relative paths
The hidden bug in os.getcwd() fallback paths - why a silent default is worse than a hard error, how the CLI layer resolves paths before calling the API, and the "fail fast on global state" design principle.
No pyrpc init needed: designing the integrated setup wizard
Why pyrpc embeds setup inside pyrpc dev instead of a separate init command: fewer context switches, --reconfigure pre-fills defaults, CLI flags skip the wizard entirely, and KeyboardInterrupt exits cleanly.
Three deployment architectures for pyrpc
Monorepo, separate repos, and published npm package - how pyrpc's config system and type generation handle all three workflows, and why server-side codegen was built before the client-side npx CLI.
Three cases, zero data loss: pyrpc's types migration strategy
What happens when you change client_root in pyrpc.json? Three cases with SHA256 comparison, interactive prompts only when needed, and a clean KeyboardInterrupt path that never leaves half-migrated state.
Path resolution in pyrpc: config-relative, not CWD-relative
Why resolving paths against pyrpc.json's directory (not os.getcwd()) is the only correct approach, how the pipeline produces absolute paths everywhere, and why save_typescript_client() enforces the contract at the boundary.
pyrpc.json: why we left pyproject.toml behind
Three problems with [tool.pyrpc] in pyproject.toml - fragile writing, ambiguous file ownership, and unclear path semantics - and why a dedicated pyrpc.json file with JSON, not TOML, was the right answer.
v0.3.3 - Cleaner types, no more /rpc/rpc, quieter watcher, CORS included
TypeScript autocomplete no longer suggests .rpc, URL normalization prevents double /rpc/rpc, file watcher debounced to 300ms, and the ASGI dev server now sends CORS headers - all following reference patterns from tRPC, Better Auth, FastAPI, webpack, and nodemon.
v0.3.2 - Cleaner terminal, smarter prompts, no more :app confusion
Interactive framework picker, simplified entry point, CWD import path fix, and a terminal that shows what matters - no Uvicorn spam, no raw [cyan] markup, no giant Panel boxes.
v0.3.1 - Lazy imports, pyrpc_codegen decoupled from CLI
pyrpc_codegen is no longer loaded for version, inspect, serve, pull, or help - only codegen and dev need it. A patch triggered by a stale shim bug.
v0.3.0 - pyrpc-cli merged into core, one-command install
pip install pyrpc-core now gives you the runtime, CLI, and codegen in a single command - no separate packages, no extra steps.
Why we merged pyrpc-cli back into pyrpc-core
How the circular dependency that motivated a three-package split disappeared - and why we simplified back to two packages for a single-install experience.
How to break a circular dependency in Python packaging
Four strategies for breaking circular package dependencies in Python, evaluated through pyrpc’s real-world restructuring — with a step-by-step extraction guide.
Windows compatibility in a Python OSS project: what we learned
Unicode crashes on cp1252, LF/CRLF git warnings, path separators, file watcher quirks, and a no-special-chars policy for cross-platform Python OSS.
Lazy imports as API contract, not performance hack
Three tiers of CLI commands, the packaging-vs-code dependency distinction, and why lazy imports define capability boundaries — not just startup time.
The Better Auth meta-package pattern, adapted for Python
How Better Auth’s npm meta-package inspired pyrpc’s package architecture, and how we adapted it for Python’s packaging constraints.
Core → CLI → Codegen: why the dependency direction matters
Why pyrpc-core → pyrpc-cli → pyrpc-codegen is the right dependency direction - and three principles for designing package chains that never tangle.
Dev console vs shell: two tools, one job, and the line between them
Why the dev console reads from the parent process (not HTTP), how the shell connects remotely, and the shared REPL UI that bridges them.
The circular dependency problem and how pyrpc-cli solved it
How we discovered and solved the circular dependency between pyrpc-core and pyrpc-codegen by extracting pyrpc-cli - with three alternative strategies evaluated and a step-by-step extraction guide.
CLI overhaul, model interfaces, and the dev tools we built
Merging pull into codegen, fixing serve, adding the dev watcher and shell REPL, and integrating jsonschema-ts for Pydantic model interfaces.
Designing the pyrpc developer console
Threads, subprocesses, and an embedded interactive console - how pyrpc dev combines a dev server, file watcher, type generator, and CLI into one terminal session.
v0.2.0 - Type safety, proper async, and @pyrpc/types
The three critical fixes that ship pyRPC v0.2.0: real type generation, working async, and a postinstall-based @pyrpc/types setup.
Cleaner codegen, one CLI, and a sharper story
Pattern A CLI, lazy pyrpc-core imports, frontend DX simplified to npm install, cross-language positioning, SECURITY.md rewrite, and Windows cp1252 fixes.
Inside the Interactive Demo Sandbox
A deep dive into how the pyrpc playground works - design decisions, architecture, and a comparison with the real pyrpc implementation.
Why pyRPC?
The philosophy behind pyRPC, what tRPC-style typing means for Python backends, and why we built it.
From raw FastAPI to pyRPC
A before-and-after migration guide showing how to convert a traditional FastAPI application to pyRPC - and why you might want to.
Building a full-stack app with pyRPC
A step-by-step tutorial: FastAPI backend, TypeScript React frontend, end-to-end type safety with pyRPC.

pyRPC