gRPC
The framework provides two actor classes for gRPC:
| Class | Role |
|---|---|
GrpcServerActor | Hosts a gRPC server; exposes service methods. |
GrpcClientActor | Connects to a remote gRPC service; calls methods. |
Both wrap @grpc/grpc-js. Useful when you have Protobuf-typed
contracts and want the broker-actor lifecycle (reconnect,
buffer, subscriber fan-out) for client-side calls.
Server
Section titled “Server”import { ActorSystem, Props, GrpcServerActor } from 'actor-ts';
const server = system.actorOf( Props.create(() => new GrpcServerActor({ host: '0.0.0.0', port: 50051, services: [ { protoPath: './proto/orders.proto', serviceName: 'orders.v1.OrderService', handler: orderHandlerRef, // actor that processes incoming calls }, ], })), 'grpc-server',);Inbound RPC calls are forwarded to handler as actor messages.
The framework de-serializes the Protobuf payload and wraps it in
an envelope.
Client
Section titled “Client”import { GrpcClientActor } from 'actor-ts';
const client = system.actorOf( Props.create(() => new GrpcClientActor({ target: 'orders.svc:50051', protoPath: './proto/orders.proto', serviceName: 'orders.v1.OrderService', })), 'orders-client',);
// Make a unary call:client.tell({ kind: 'unary', method: 'GetOrder', request: { id: 'order-42' }, replyTo: this.self,});
// Receive the response:override onReceive(msg: GrpcReply) { if (msg.kind === 'reply') handleOrder(msg.response); if (msg.kind === 'error') handleError(msg.error);}The actor handles connection management, retries, and routing the reply to whoever asked.
Streaming modes
Section titled “Streaming modes”gRPC supports four call types; the framework handles all:
| Type | Client sends | Server returns |
|---|---|---|
| Unary | One request | One response |
| Server-streaming | One request | Stream of responses |
| Client-streaming | Stream of requests | One response |
| Bi-directional streaming | Stream of requests | Stream of responses |
// Server-streaming:client.tell({ kind: 'server-stream', method: 'WatchOrders', request: { customerId: '42' }, subscriber: streamHandler, // gets each response});
// Bi-directional:client.tell({ kind: 'bidi-stream', method: 'Chat', subscriber: chatHandler,});// Send messages into the stream:client.tell({ kind: 'stream-send', method: 'Chat', message: { text: 'hello' } });Streams stay open until either side closes them or the actor stops.
new GrpcServerActor({ host: '0.0.0.0', port: 50051, tls: { cert: fs.readFileSync('./server.crt'), key: fs.readFileSync('./server.key'), }, services: [...],});
new GrpcClientActor({ target: 'orders.svc:50051', tls: { ca: fs.readFileSync('./ca.crt'), }, ...});Standard TLS configuration via cert / key / ca. For
mutual TLS (mTLS), provide the client’s cert + key.
Peer dependency
Section titled “Peer dependency”npm install @grpc/grpc-js @grpc/proto-loader# or: bun add @grpc/grpc-js @grpc/proto-loaderBoth packages are peer dependencies.
When to use gRPC
Section titled “When to use gRPC”Two primary fits:
- Service-to-service inside a cluster where Protobuf-typed contracts matter for evolution.
- External clients that already speak gRPC (mobile apps, other-language services).
For internal actor-to-actor communication inside a cluster, the cluster transport is better — typed via TypeScript directly, no Protobuf required. gRPC is for cross-language or external-contract cases.
Where to next
Section titled “Where to next”- I/O overview — the bigger picture.
- BrokerActor base — the shared lifecycle.
- Refs across nodes — for cluster-internal RPC (TypeScript-typed alternative).
- HTTP overview — for HTTP-based RPC instead.