website/src/content/docs/actors/communicating-between-actors.mdx
Actors can communicate with each other using the server-side actor client, enabling complex workflows and data sharing between different actor instances.
<Note> We recommend reading the [clients documentation](/docs/clients) first. This guide focuses specifically on communication between actors. </Note>The server-side actor client allows actors to call other actors within the same registry. Access it via c.client() in your actor context:
import { actor, setup } from "rivetkit";
interface Order {
id: string;
customerId: string;
quantity: number;
amount: number;
}
interface ProcessedOrder extends Order {
status: string;
paymentResult: { transactionId: string };
}
const inventory = actor({
state: { stock: 100 },
actions: {
reserveStock: (c, quantity: number) => {
c.state.stock -= quantity;
return { reserved: quantity };
}
}
});
const payment = actor({
state: {},
actions: {
processPayment: (c, amount: number) => ({ transactionId: "tx-123" })
}
});
const orderProcessor = actor({
state: { orders: [] as ProcessedOrder[] },
actions: {
processOrder: async (c, order: Order) => {
const client = c.client<typeof registry>();
// Reserve the stock
const inventoryHandle = client.inventory.getOrCreate(["main"]);
await inventoryHandle.reserveStock(order.quantity);
// Process payment through payment actor
const paymentHandle = client.payment.getOrCreate([order.customerId]);
const result = await paymentHandle.processPayment(order.amount);
// Update order state
c.state.orders.push({ ...order, status: "completed", paymentResult: result });
return { success: true, orderId: order.id };
}
}
});
const registry = setup({ use: { inventory, payment, orderProcessor } });
Use a coordinator actor to manage complex workflows:
import { actor, setup } from "rivetkit";
interface WorkflowResult {
workflowId: string;
result: { finalized: boolean };
completedAt: number;
}
const dataProcessor = actor({
state: {},
actions: {
initialize: (c, workflowId: string) => ({ workflowId, data: "initialized" })
}
});
const validator = actor({
state: {},
actions: {
validate: (c, data: { workflowId: string; data: string }) => ({ valid: true, data })
}
});
const finalizer = actor({
state: {},
actions: {
finalize: (c, validationResult: { valid: boolean }) => ({ finalized: validationResult.valid })
}
});
const workflowActor = actor({
state: { workflows: [] as WorkflowResult[] },
actions: {
executeWorkflow: async (c, workflowId: string) => {
const client = c.client<typeof registry>();
// Step 1: Initialize data
const dataProcessorHandle = client.dataProcessor.getOrCreate(["main"]);
const data = await dataProcessorHandle.initialize(workflowId);
// Step 2: Process through multiple actors
const validatorHandle = client.validator.getOrCreate(["main"]);
const validationResult = await validatorHandle.validate(data);
// Step 3: Finalize
const finalizerHandle = client.finalizer.getOrCreate(["main"]);
const result = await finalizerHandle.finalize(validationResult);
c.state.workflows.push({ workflowId, result, completedAt: Date.now() });
return result;
}
}
});
const registry = setup({ use: { dataProcessor, validator, finalizer, workflowActor } });
Collect data from multiple actors:
import { actor, setup } from "rivetkit";
interface Stats {
count: number;
total: number;
}
interface Report {
id: string;
type: string;
data: { users: Stats; orders: Stats; system: Stats };
generatedAt: number;
}
const userMetrics = actor({
state: {},
actions: {
getStats: (c): Stats => ({ count: 100, total: 500 })
}
});
const orderMetrics = actor({
state: {},
actions: {
getStats: (c): Stats => ({ count: 50, total: 10000 })
}
});
const systemMetrics = actor({
state: {},
actions: {
getStats: (c): Stats => ({ count: 5, total: 99 })
}
});
const analyticsActor = actor({
state: { reports: [] as Report[] },
actions: {
generateReport: async (c, reportType: string) => {
const client = c.client<typeof registry>();
// Collect data from multiple sources
const userMetricsHandle = client.userMetrics.getOrCreate(["main"]);
const orderMetricsHandle = client.orderMetrics.getOrCreate(["main"]);
const systemMetricsHandle = client.systemMetrics.getOrCreate(["main"]);
const [users, orders, system] = await Promise.all([
userMetricsHandle.getStats(),
orderMetricsHandle.getStats(),
systemMetricsHandle.getStats()
]);
const report: Report = {
id: crypto.randomUUID(),
type: reportType,
data: { users, orders, system },
generatedAt: Date.now()
};
c.state.reports.push(report);
return report;
}
}
});
const registry = setup({ use: { userMetrics, orderMetrics, systemMetrics, analyticsActor } });
Use connections to listen for events from other actors:
import { actor, setup } from "rivetkit";
interface User {
id: string;
name: string;
}
interface Order {
id: string;
amount: number;
}
interface AuditLog {
event: string;
data: User | Order;
timestamp: number;
}
const userActor = actor({
state: {},
actions: {
createUser: (c, name: string) => {
const user = { id: crypto.randomUUID(), name };
c.broadcast("userCreated", user);
return user;
}
}
});
const orderActor = actor({
state: {},
actions: {
completeOrder: (c, amount: number) => {
const order = { id: crypto.randomUUID(), amount };
c.broadcast("orderCompleted", order);
return order;
}
}
});
const auditLogActor = actor({
state: { logs: [] as AuditLog[] },
actions: {
startAuditing: async (c) => {
const client = c.client<typeof registry>();
// Connect to multiple actors to listen for events
const userActorConn = client.userActor.getOrCreate(["main"]).connect();
const orderActorConn = client.orderActor.getOrCreate(["main"]).connect();
// Listen for user events
userActorConn.on("userCreated", (user: User) => {
c.state.logs.push({
event: "userCreated",
data: user,
timestamp: Date.now()
});
});
// Listen for order events
orderActorConn.on("orderCompleted", (order: Order) => {
c.state.logs.push({
event: "orderCompleted",
data: order,
timestamp: Date.now()
});
});
return { status: "auditing started" };
}
}
});
const registry = setup({ use: { userActor, orderActor, auditLogActor } });
Process multiple items in parallel:
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";
interface Item {
type: string;
data: string;
}
const processor = actor({
state: {},
actions: {
process: (c, item: Item) => ({ processed: true, item })
}
});
const registry = setup({ use: { processor } });
const client = createClient<typeof registry>("http://localhost:6420");
// Process items in parallel
const items: Item[] = [
{ type: "typeA", data: "data1" },
{ type: "typeB", data: "data2" }
];
const results = await Promise.all(
items.map(item => client.processor.getOrCreate([item.type]).process(item))
);
ActorHandle - Handle for calling other actorsClient - Client type for actor communicationActorAccessor - Accessor for getting actor handles