Sign In
More

Communicating Between Actors

Learn how actors can call other actors and share data

Actors can communicate with each other using the inline client, enabling complex workflows and data sharing between different actor instances.


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:

TypeScript
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.

TypeScript
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:

TypeScript
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:

TypeScript
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:

TypeScript
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:

TypeScript
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;
    }
  }
});

Performance Optimization

Batch Operations: Process multiple items in parallel:

TypeScript
// 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:

TypeScript
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:

TypeScript
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
);
Suggest changes to this page