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 valueconst value = await this.state.storage.get<string>("user:name");
// Get multiple valuesconst 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 keysconst allData = await this.state.storage.list();
// List with prefix filterconst userData = await this.state.storage.list({ prefix: "user:" });
// List with paginationconst 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 prefixreverse
: List in reverse orderlimit
: 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 valueawait this.state.storage.put("user:123", { name: "John", active: true });
// Store multiple valuesawait this.state.storage.put({ "user:123": userData, "session:abc": sessionData});
Parameters:
key
: String key or object with key-value pairsvalue
: 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 keyconst deleted = await this.state.storage.delete("user:123");
// Delete multiple keysconst 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 nowconst future = Date.now() + 60 * 60 * 1000;await this.state.storage.setAlarm(future);
// Schedule with Date objectawait 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 stringconst actorId = this.state.id.toString();
// Check if IDs are equalconst isSameActor = this.state.id.equals(otherActorId);
// Get optional nameconst actorName = this.state.id.name;
Properties:
toString()
: Returns string representation of the actor ID for logging and debuggingequals(other)
: Compares with another ActorId to check if they reference the same actor instancename
: Optional human-readable name if the ID was created usingidFromName()
method