pyRPC
← Back to Blog

@rpc.query and @rpc.mutation: why procedure kinds exist

·10 min read

TanStack Query has two verbs: read (useQuery) and write (useMutation). JSON-RPC does not. So pyRPC adds an optional kind on each procedure — declared on the server, shipped through codegen, enforced on the typed hooks.

Server

from pyrpc_core import rpc

@rpc.query
def get_user(user_id: int) -> dict:
    ...

@rpc.mutation
def update_user(user_id: int, name: str) -> dict:
    ...

# Bare @rpc still works → kind "query" (backward compatible)

What you import

You import Types only. Codegen brands each procedure with _pyrpcKind and emits a runtime map adapters load automatically. No second “kinds” import in app code.

Why not only client-side hooks?

Allowing every procedure to expose both hooks forever works, but lies to the type system. Declaring kind on the server makes the contract honest: a mutation is not a cacheable query key, and autocomplete shows the right hook.

Wire format

Kind does not change JSON-RPC. It is metadata for codegen and adapters. The POST body is still { id, method, params }.

Procedures docs · Adapters deep dive