How to use RPC in Base

Call a service method like it is local, and let the framework carry the call across the wire.

Building APIs
AhraJuly 22, 2026

How to use RPC in Base

REST and GraphQL are how the outside world talks to your worker over a described HTTP surface. RPC is for when you want to call a worker the way you call a function: name the procedure, pass typed arguments, get a typed result, without designing a URL scheme or a schema in between. Base's RPC keeps the call typed end to end, so the caller and the callee cannot quietly disagree about the shape of a procedure.

Declare an RPC service

An RPC service is a class whose methods are callable procedures. You mark the class as an RPC service and mark the methods that should be exposed:

@RpcService()
export class UserRpc {
    @Rpc()
    async getUser(@RpcArgument() id: string): Promise<User> {
        // ...
    }
}

@Rpc() exposes a method as a procedure; @RpcArgument() declares its inputs. From the caller's side, calling getUser looks like calling a method, and the types of its arguments and its result are the types you declared here. There is no hand-written request payload or response parser in between.

Public or internal: who is allowed to call

The most important decision about an RPC service is its visibility, and Base makes it explicit. A service is either:

  • public, meaning it is callable from a public route, so a client outside your worker (like a browser) can reach it; or
  • internal, meaning it is only callable from a bound worker, so the procedure is reachable worker-to-worker but not exposed to the outside world.

This single setting is what separates the two big RPC use cases. Internal RPC is your private back-of-house API between your own workers. Public RPC is a typed call surface you deliberately open to clients. Choosing visibility is choosing your audience, and because it is declared rather than implied, you do not accidentally expose an internal procedure to the public internet.

Calling out

To call another service's procedures, you inject a client for it with @InjectRpcClient. The client is typed against the service it calls, so the procedures you can invoke, their arguments, and their return types are all known at compile time. Calling a procedure whose shape changed becomes a type error in the caller, not a runtime failure in production.

Under the hood, Base has more than one way to carry an RPC call (over a fetch-style request, or over a websocket connection), but the call you write looks the same regardless. The transport is the framework's concern; the typed procedure is yours.

The mental model to keep

RPC in Base is calling a worker like a function: @RpcService classes with @Rpc procedures and @RpcArgument inputs, typed end to end. Visibility (public for clients, internal for worker-to-worker) decides who can call. You reach another service through an injected, typed client. The dedicated posts on worker-to-worker and web-to-worker RPC walk those two audiences in detail; this is the shape they share.

How to use RPC in Base • System