Skip to content

gRPC

The framework provides two actor classes for gRPC:

ClassRole
GrpcServerActorHosts a gRPC server; exposes service methods.
GrpcClientActorConnects 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.

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.

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.

gRPC supports four call types; the framework handles all:

TypeClient sendsServer returns
UnaryOne requestOne response
Server-streamingOne requestStream of responses
Client-streamingStream of requestsOne response
Bi-directional streamingStream of requestsStream 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.

Terminal window
npm install @grpc/grpc-js @grpc/proto-loader
# or: bun add @grpc/grpc-js @grpc/proto-loader

Both packages are peer dependencies.

Two primary fits:

  1. Service-to-service inside a cluster where Protobuf-typed contracts matter for evolution.
  2. 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.