Communication

Communicating Between Actors

Learn how actors can call other actors and share data

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

We recommend reading the clients documentation first. This guide focuses specifically on communication between actors.

Using the Server-Side Actor Client

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

Use Cases and Patterns

Actor Orchestration

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

Data Aggregation

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

Event-Driven Architecture

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

Batch Operations

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>();

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

API Reference