Actor Keys
Actor keys uniquely identify actor instances within each actor type. Keys are used for addressing which specific actor to communicate with.
Key Format
Actor keys can be either a string or an array of strings:
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";
const counter = actor({
state: { count: 0 },
actions: { increment: (c) => c.state.count++ }
});
const chatRoom = actor({
state: { messages: [] as string[] },
actions: {}
});
const registry = setup({ use: { counter, chatRoom } });
const client = createClient<typeof registry>();
// String key
const counterHandle = client.counter.getOrCreate(["my-counter"]);
// Array key (compound key)
const chatRoomHandle = client.chatRoom.getOrCreate(["room", "general"]);
Compound Keys & User Data
Array keys are useful when you need compound keys with user-provided data. Using arrays makes adding user data safe by preventing key injection attacks:
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";
const chatRoom = actor({ state: { messages: [] as string[] }, actions: {} });
const gameRoom = actor({ state: { players: [] as string[] }, actions: {} });
const workspace = actor({ state: { data: {} }, actions: {} });
const registry = setup({ use: { chatRoom, gameRoom, workspace } });
const client = createClient<typeof registry>();
// Example user data
const userId = "user-123";
const gameId = "game-456";
const tenantId = "tenant-789";
const workspaceId = "workspace-abc";
// User-specific chat rooms
const userRoomHandle = client.chatRoom.getOrCreate(["user", userId, "private"]);
// Game rooms by region and difficulty
const gameRoomHandle = client.gameRoom.getOrCreate(["us-west", "hard", gameId]);
// Multi-tenant resources
const workspaceHandle = client.workspace.getOrCreate(["tenant", tenantId, workspaceId]);
This allows you to create hierarchical addressing schemes and organize actors by multiple dimensions.
Don’t build keys using string interpolation like "foo:${userId}:bar" when userId contains user data. If a user provides a value containing the delimiter (: in this example), it can break your key structure and cause key injection attacks.
Omitting Keys
You can create actors without specifying a key in situations where there is a singleton actor (i.e. only one actor of a given type). For example:
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";
const globalActor = actor({
state: { config: {} },
actions: {}
});
const registry = setup({ use: { globalActor } });
const client = createClient<typeof registry>();
// Get the singleton session
const globalActorHandle = client.globalActor.getOrCreate();
This pattern should be avoided, since a singleton actor usually means you have a single actor serving all traffic & your application will not scale. See scaling documentation for more information.
Key Uniqueness
Keys are unique within each actor name. Different actor types can use the same key:
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";
const chatRoom = actor({ state: { messages: [] as string[] }, actions: {} });
const userProfile = actor({ state: { name: "" }, actions: {} });
const registry = setup({ use: { chatRoom, userProfile } });
const client = createClient<typeof registry>();
// These are different actors, same key is fine
const userChat = client.chatRoom.getOrCreate(["user-123"]);
const userProfileHandle = client.userProfile.getOrCreate(["user-123"]);
Accessing Keys in Metadata
Access the actor’s key within the actor using the metadata API:
import { actor, setup } from "rivetkit";
const chatRoom = actor({
state: { messages: [] as string[] },
actions: {
getRoomName: (c) => {
// Access the key from metadata
const key = c.key;
return key[1]; // Get "general" from ["room", "general"]
}
}
});
export const registry = setup({
use: { chatRoom }
});
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";
const chatRoom = actor({
state: { messages: [] as string[] },
actions: { getRoomName: (c) => c.key[1] }
});
const registry = setup({ use: { chatRoom } });
const client = createClient<typeof registry>("http://localhost:8080");
async function connectToRoom(roomName: string) {
// Connect to a chat room
const chatRoomHandle = client.chatRoom.getOrCreate(["room", roomName]);
// Get the room name from the key
const retrievedRoomName = await chatRoomHandle.getRoomName();
console.log("Room name:", retrievedRoomName); // e.g., "general"
return chatRoomHandle;
}
// Usage example
const generalRoom = await connectToRoom("general");
Configuration Examples
Simple Configuration with Keys
Use keys to provide basic actor configuration:
import { actor, setup } from "rivetkit";
interface UserSessionState {
userId: string;
loginTime: number;
preferences: Record<string, unknown>;
}
const userSession = actor({
state: { userId: "", loginTime: 0, preferences: {} } as UserSessionState,
createState: (c): UserSessionState => ({
userId: c.key[0], // Extract user ID from key
loginTime: Date.now(),
preferences: {}
}),
actions: {
getUserId: (c) => c.state.userId
}
});
export const registry = setup({
use: { userSession }
});
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";
const userSession = actor({
state: { userId: "", loginTime: 0, preferences: {} },
actions: { getUserId: (c) => c.state.userId }
});
const registry = setup({ use: { userSession } });
const client = createClient<typeof registry>("http://localhost:8080");
// Pass user ID in the key for user-specific actors
const userId = "user-123";
const userSessionHandle = client.userSession.getOrCreate([userId]);
Complex Configuration with Input
For more complex configuration, use input parameters:
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";
interface ChatRoomInput {
maxUsers: number;
isPrivate: boolean;
moderators: string[];
settings: { allowImages: boolean; slowMode: boolean };
}
const chatRoom = actor({
state: { maxUsers: 0, isPrivate: false, moderators: [] as string[], settings: { allowImages: true, slowMode: false } },
createState: (c, input: ChatRoomInput) => ({
maxUsers: input.maxUsers,
isPrivate: input.isPrivate,
moderators: input.moderators,
settings: input.settings,
}),
actions: {}
});
const registry = setup({ use: { chatRoom } });
const client = createClient<typeof registry>("http://localhost:8080");
const roomName = "general";
// Create with both key and input
const chatRoomHandle = await client.chatRoom.create(["room", roomName], {
input: {
maxUsers: 100,
isPrivate: false,
moderators: ["admin1", "admin2"],
settings: {
allowImages: true,
slowMode: false
}
}
});
API Reference
ActorKey- Key type for actorsActorQuery- Query type using keysGetOptions- Options for getting by keyQueryOptions- Options for querying