pyRPC
← Back to Blog

The tiny change in @pyrpc/types that powers everything

·6 min read

The @pyrpc/types package used to export one thing: a placeholder Types interface. Codegen overwrote it. In v0.9.0, we added two more exports — and they are the glue between codegen and the adapters.

Before v0.9.0

// node_modules/@pyrpc/types/src/index.ts (placeholder)
export type Types = Record<string, never>

After codegen runs, this file is overwritten with your actual procedures. The adapters import Types from here.

After v0.9.0

export type Types = Record<string, never>

/** @internal Runtime kind map — loaded automatically by framework adapters. */
export type ProcedureKinds = Record<string, never>

/** @internal Runtime kind map — loaded automatically by framework adapters. */
export const procedureKinds = {} as const satisfies ProcedureKinds

Two additions: ProcedureKinds (a type) and procedureKinds (a const value). Both are placeholders that codegen overwrites.

Why both a type and a const

TypeScript types are erased at runtime. If codegen only emitted type ProcedureKinds = { ... }, the adapter could not read the kinds at runtime. The const procedureKinds is the actual value the adapter's Proxy handler checks.

The as const satisfies ProcedureKinds pattern ensures the const matches the type at compile time while preserving literal types.

What codegen produces

export type ProcedureKinds = {
  greet: "query"
  get_user: "query"
  update_user: "mutation"
}

export const procedureKinds = {
  greet: "query",
  get_user: "query",
  update_user: "mutation",
} as const satisfies ProcedureKinds

Why this is in @pyrpc/types, not a separate package

The adapters already import Types from @pyrpc/types. Adding ProcedureKinds to the same file means one import, one package, one codegen step. No new dependencies for users.

How procedure kinds flow end-to-end · Client docs