Actors can communicate with each other using the inline client, enabling complex workflows and data sharing between different actor instances.
This guide focuses on communication between actors within the same application. For connecting to actors from client applications, see Communicating with Actors.
Using the Inline Client
The inline client allows actors to call other actors within the same registry. Access it via c.client()
in your actor actions:
import { actor } from "@rivetkit/actor";
import type { registry } from "./registry";
export const orderProcessor = actor({
state: { orders: [] as Order[] },
actions: {
processOrder: async (c, order: Order) => {
// Get the inline client with registry types
const client = c.client<typeof registry>();
// Call another actor to check inventory
const inventory = client.inventory.getOrCreate([order.productId]);
const available = await inventory.checkStock(order.quantity);
if (!available) {
throw new UserError("Insufficient stock");
}
// Reserve the stock
await inventory.reserveStock(order.quantity);
// Process payment through payment actor
const payment = client.payment.getOrCreate([order.customerId]);
const result = await payment.processPayment(order.amount);
// Update order state
c.state.orders.push({
...order,
status: "processed",
paymentId: result.paymentId,
});
return { success: true, orderId: order.id };
}
}
});
Communication Patterns
The inline client supports the same communication patterns as external clients. See Communicating with Actors - Actor Handles for details on:
getOrCreate()
for stateless request-response
.connect()
for real-time communication with events
get()
and create()
for explicit actor lifecycle management
Error Handling
Handle errors gracefully when calling other actors. Error handling works the same as with external clients - see Communicating with Actors - Error Handling for details.
export const orderActor = actor({
state: { orders: [] },
actions: {
createOrder: async (c, orderData: CreateOrderData) => {
const client = c.client<typeof registry>();
try {
const inventory = client.inventory.getOrCreate([orderData.productId]);
await inventory.validateOrder(orderData);
const order = { id: generateId(), ...orderData, status: "validated" };
c.state.orders.push(order);
return order;
} catch (error) {
throw new UserError(`Order validation failed: ${error.message}`);
}
}
}
});
Use Cases and Patterns
Actor Orchestration
Use a coordinator actor to manage complex workflows:
export const workflowActor = actor({
state: { workflows: new Map() },
actions: {
executeUserOnboarding: async (c, userId: string) => {
const client = c.client<typeof registry>();
// Coordinate multiple actors for complex workflow
const user = client.user.getOrCreate([userId]);
await user.createProfile();
const notification = client.notification.getOrCreate(["system"]);
await notification.sendWelcomeEmail(userId);
const preferences = client.preferences.getOrCreate([userId]);
await preferences.setDefaults();
return { status: "completed" };
}
}
});
Data Aggregation
Collect data from multiple actors:
export const analyticsActor = actor({
state: { reports: [] },
actions: {
generateUserReport: async (c, userId: string) => {
const client = c.client<typeof registry>();
// Collect data from multiple sources in parallel
const [profile, orders, preferences] = await Promise.all([
client.user.getOrCreate([userId]).getProfile(),
client.orders.getOrCreate([userId]).getHistory(),
client.preferences.getOrCreate([userId]).getAll(),
]);
return { profile, orders, preferences };
}
}
});
Event-Driven Architecture
Use connections to listen for events from other actors:
export const auditLogActor = actor({
state: { logs: [] },
actions: {
startAuditing: async (c, userId: string) => {
const client = c.client<typeof registry>();
const user = client.user.getOrCreate([userId]);
const connection = user.connect();
// Listen for events from the user actor
connection.on("profileUpdated", (profile) => {
c.state.logs.push({
type: "profile_update",
userId,
timestamp: Date.now(),
data: profile,
});
});
return { status: "auditing_started" };
}
}
});
Advanced Features
Type Safety
The inline client maintains full type safety across actor boundaries:
export const typedActor = actor({
actions: {
processData: async (c) => {
const client = c.client<typeof registry>();
// TypeScript validates action signatures
const counter = client.counter.getOrCreate(["stats"]);
const count: number = await counter.increment(1); // ✓
const invalid = await counter.increment("1"); // ✗ Type error
return count;
}
}
});
Batch Operations: Process multiple items in parallel:
// Process items in parallel
const results = await Promise.all(
items.map(item => client.processor.getOrCreate([item.type]).process(item))
);
Connection Reuse: Reuse connections for multiple operations:
const connection = client.targetActor.getOrCreate(["shared"]).connect();
try {
for (const op of operations) {
await connection.performOperation(op);
}
} finally {
await connection.dispose();
}
Testing
Mock the inline client for unit testing:
const mockClient = {
inventory: {
getOrCreate: jest.fn().mockReturnValue({
checkStock: jest.fn().mockResolvedValue(true),
}),
},
};
// Test with mocked dependencies
const result = await orderProcessor.processOrder.call(
{ client: () => mockClient },
orderData
);