Skip to content

Actor

Overview

Actors are stateful compute units that maintain persistent data and handle requests with a unique identity. Unlike stateless services that lose data between requests, each actor instance remembers its state and can coordinate complex workflows over time.

Think of actors as persistent “mini-services” - each user session, shopping cart, or game room can be its own actor instance. When a request comes in, you route it to the specific actor by ID, and that actor maintains all the relevant state for that user, cart, or room.

Key Benefits:

  • Persistent state across requests without external databases for simple use cases
  • Unique identity allows you to route related requests to the same instance
  • Scheduled operations with built-in alarm system for timeouts and cleanup
  • Isolation between different actor instances

Common Use Cases:

  • User sessions and authentication state
  • Shopping carts and temporary user data
  • Chat rooms and collaborative spaces
  • Workflow coordination and multi-step processes
  • Rate limiting and user-specific counters

Setup Flow

1. Configure in Manifest

Add an actor and service to your Raindrop project:

application "actor-demo" {
actor "actor-demo" {}
}

2. Generate Actor Code

Run raindrop build generate to auto-generate the actor implementation. This creates the actor class with all the storage, alarm, and identity functionality.

export class ActorTest extends Actor<Env> {
async fetch(request: Request): Promise<Response> {
return new Response('fetch method not implemented for actors', { status: 501 });
}
// Create custom methods that services can call
async handleUserRequest(request: Request): Promise<Response> {
// Your actor logic here
return new Response('Actor handled request');
}
}

3. Access Through Services

Services act as the public gateway to your actors. When a request comes in, the service determines which actor instance should handle it (usually based on user ID, session, etc.) and calls the appropriate method on that actor. This allows you to have many isolated actor instances, each maintaining their own state.

export default class extends Service<Env> {
async fetch(request: Request): Promise<Response> {
// Route to user-specific actor instance
const userId = getUserIdFromRequest(request);
const actorId = this.env.ACTOR_TEST.idFromName(userId);
const actor = this.env.ACTOR_TEST.get(actorId);
// Call custom actor methods (NOT fetch)
return await actor.handleUserRequest(request);
}
}

Actor State

The actor state provides access to persistent storage, alarm scheduling, and concurrency controls that persist across requests.

state.storage

Access persistent storage for the actor instance. Store and retrieve data that persists across different requests to the same actor.

get(key)

Retrieves a value by key from actor storage. This method supports both single key lookups and batch retrieval for performance optimization. When retrieving multiple keys, the operation is atomic and returns a Map containing all found values. The generic type parameter ensures type safety when you know the expected data structure. Values are automatically deserialized from their stored format, allowing you to store and retrieve complex objects seamlessly.

// Get single value
const value = await this.state.storage.get<string>("user:name");
// Get multiple values
const values = await this.state.storage.get<string>(["key1", "key2"]);

Parameters:

  • key: String key or array of keys to retrieve
  • Generic type T: Expected value type

Returns: Single value, undefined, or Map<string, T> for multiple keys

list(options?)

Lists stored keys with optional filtering and pagination. This method is essential for discovering data in your actor’s storage, implementing search functionality, or performing bulk operations. The filtering options allow you to efficiently work with namespaced keys (using prefixes) and handle large datasets through pagination. The reverse option is particularly useful for time-based keys where you want the most recent entries first.

// List all keys
const allData = await this.state.storage.list();
// List with prefix filter
const userData = await this.state.storage.list({ prefix: "user:" });
// List with pagination
const batch = await this.state.storage.list({
prefix: "session:",
limit: 100,
start: "session:abc"
});

Options:

  • start: Start listing from this key (inclusive)
  • startAfter: Start listing after this key (exclusive)
  • end: Stop listing at this key (exclusive)
  • prefix: Filter keys by prefix
  • reverse: List in reverse order
  • limit: Maximum number of keys to return

Returns: Map<string, T> of key-value pairs

put(key, value)

Stores a value with the given key. This method handles both single value storage and bulk operations. The storage persists until explicitly deleted.

// Store single value
await this.state.storage.put("user:123", { name: "John", active: true });
// Store multiple values
await this.state.storage.put({
"user:123": userData,
"session:abc": sessionData
});

Parameters:

  • key: String key or object with key-value pairs
  • value: Value to store (for single key)

delete(key)

Removes keys from storage. The method returns different types based on the input: a boolean for single keys indicating whether the key existed, and a count for multiple keys showing how many were actually deleted. Deleting non-existent keys is safe and won’t throw errors.

// Delete single key
const deleted = await this.state.storage.delete("user:123");
// Delete multiple keys
const deletedCount = await this.state.storage.delete(["key1", "key2"]);

Parameters:

  • key: String key or array of keys to delete

Returns: Boolean (single key) or number of deleted keys (multiple)

Alarm Management

Schedule time-based operations that trigger automatically. Perfect for implementing timeouts, cleanup tasks, or periodic processing. When an alarm triggers, it calls the alarm() method on your actor, allowing you to implement custom cleanup logic, send notifications, or perform any time-based operations.

getAlarm()

Retrieves the currently scheduled alarm time. This method allows you to check if an alarm is pending and when it will trigger.

const alarmTime = await this.state.storage.getAlarm();
if (alarmTime) {
console.log(`Alarm scheduled for: ${new Date(alarmTime)}`);
}

Returns: Unix timestamp (number) or null if no alarm is set

setAlarm(scheduledTime)

Schedules an alarm to trigger at the specified time. When the alarm fires, your actor’s alarm() method will be called automatically. This is useful for implementing timeouts, session expiration, cleanup tasks, or any time-based logic.

// Schedule alarm for 1 hour from now
const future = Date.now() + 60 * 60 * 1000;
await this.state.storage.setAlarm(future);
// Schedule with Date object
await this.state.storage.setAlarm(new Date('2024-12-31T23:59:59Z'));

Parameters:

  • scheduledTime: Unix timestamp (number) or Date object

deleteAlarm()

Cancels the currently scheduled alarm. This is useful when conditions change and you no longer need the scheduled operation to occur.

await this.state.storage.deleteAlarm();

Actor Identity

Access the unique identifier that distinguishes this actor instance from others. Useful for logging, debugging, and coordinating between multiple actors. Each actor instance has a globally unique ID that remains constant throughout its lifetime. This ID is generated when you first access an actor instance and allows the Raindrop runtime to route requests to the correct actor instance and maintain its persistent state. The ID can be created from custom strings (like user IDs) to ensure predictable routing, or generated randomly for ephemeral use cases.

state.id

Access the unique identifier for this actor instance. The actor ID is crucial for understanding which specific actor instance is handling a request and for implementing coordination between multiple actors. The ID is immutable once created and serves as the primary key for routing requests to the correct actor instance. You can use the ID for logging, debugging, or implementing inter-actor communication patterns where actors need to reference each other.

// Get actor ID as string
const actorId = this.state.id.toString();
// Check if IDs are equal
const isSameActor = this.state.id.equals(otherActorId);
// Get optional name
const actorName = this.state.id.name;

Properties:

  • toString(): Returns string representation of the actor ID for logging and debugging
  • equals(other): Compares with another ActorId to check if they reference the same actor instance
  • name: Optional human-readable name if the ID was created using idFromName() method