xx
.md

xxrpc_handler

xxrpc_handler generates Protobuf-ES message sources and abstract TypeScript handler classes for unary, client-streaming, server-streaming, and bidirectional RPC methods. Generation runs during xx run; pass the returned dependency to deps when another task needs the generated files.

Arguments

ArgumentRequiredDescription
srcsYesNon-empty list of project-relative .proto files.
out_esYesProject-relative ECMAScript output directory.
depsNoDependencies that provide protoc and protoc-gen-es. Defaults to [].

Load protoc from an exact @protobuf package. Install @bufbuild/protoc-gen-es version 2 in the project and make node_modules/.bin available through deps. xxRPC checks for protoc-gen-es before invoking protoc and does not install or select a plugin version.

Each generated <source>_xxrpc.ts file exports one abstract <Service>Handler class per Protobuf service. Handler implementations use only standard TypeScript types and generated Protobuf messages:

Protobuf methodHandler inputHandler result
UnaryInputPromise<Output>
Server streamingInputAsyncIterable<Output>
Client streamingAsyncIterable<Input>Promise<Output>
Bidirectional streamingAsyncIterable<Input>AsyncIterable<Output>

Every method also receives an XXRPCContext containing an AbortSignal. The signal is aborted when the request or WebSocket disconnects or the client cancels. Implementations do not import Node.js, Cloudflare, Vercel, or another server framework.

The generated handle(request: Request) method handles unary calls:

The generated prepareStreaming(request) method validates a WebSocket request before an adapter upgrades it. The request must use xxrpc.streaming.v1, include exactly one xxrpc-service and xxrpc-method URL parameter, and name a streaming method. Requesting a unary method through WebSocket or a streaming method through HTTP returns HTTP 400.

XXRPCPreparedStream accepts a small XXRPCSocket interface. Platform adapters only translate their WebSocket implementation to this interface; Protobuf dispatch, flow control, cancellation, and errors remain in the generated generic core.

Cloudflare Workers

Handler generation also writes <source>_xxrpc_cloudflare.ts. It exports createCloudflareXXRPCHandler, which handles both standard Fetch requests and WebSocketPair upgrades:

import { Greeter } from "./greeter.js";
import { createCloudflareXXRPCHandler } from "./generated/contracts/greeter_xxrpc_cloudflare.js";

const handle = createCloudflareXXRPCHandler(new Greeter());

export default {
  fetch(request: Request): Promise<Response> {
    return handle(request);
  },
};

The adapter is generated separately so the generic handler module contains no Cloudflare APIs or type dependencies. Authentication and origin checks should run before calling the generated adapter.

Example

load("@[email protected]", "npm_install")
load("@os@1", "env_prepend")
load("@[email protected]", "protoc")
load("@xxrpc@1", "xxrpc_handler")

install = npm_install()

rpc_server = xxrpc_handler(
    srcs=["contracts/greeter.proto"],
    out_es="backend/generated",
    deps=[
        protoc,
        install,
        env_prepend("PATH", "node_modules/.bin"),
    ],
)

Sources retain their path below the project root. contracts/greeter.proto produces these files:

The output directory is owned by the xxRPC task. Obsolete generated files from preceding generations are removed before generation.