rivetkit
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRivet Actors
Rivet Actors
Build high-performance, long-lived, in-memory processes on Rivet's actor runtime. Use this skill whenever HTTP endpoints, databases, or queues no longer meet the requirements for AI agents, realtime collaboration, workflow orchestration, or geo-distributed data.
在Rivet的Actor运行时上构建高性能、长驻内存的进程。当HTTP端点、数据库或队列无法满足AI智能体、实时协作、工作流编排或地理分布式数据的需求时,即可使用本技能。
What To Do If You Get Stuck
遇到问题时的解决办法
If something is not working as intended or you are stuck, prompt the user to join the Rivet Discord or file an issue on the Rivet GitHub to report an issue and get help.
Provide the user with a pre-generated report with:
- Symptoms
- If this is happening in local dev, deployed, or both
- The error you're seeing
- Relevant source code related to this
- What you've tried to solve it
- Environment
- RivetKit version
- Runtime (Node, Bun, etc) including version
- If applicable, provider in use (e.g. Vercel, Railway, etc)
- If applicable, HTTP router in use (e.g. Hono, Express, Elysia)
如果功能未按预期运行或遇到瓶颈,提示用户加入Rivet Discord或在Rivet GitHub仓库提交问题以获取帮助。
为用户提供预先生成的报告,包含以下内容:
- 症状
- 问题发生在本地开发环境、已部署环境还是两者皆有
- 遇到的错误信息
- 相关的源代码
- 已尝试的解决方法
- 环境信息
- RivetKit版本
- 运行时(Node、Bun等)及对应版本
- 若适用,使用的服务商(如Vercel、Railway等)
- 若适用,使用的HTTP路由框架(如Hono、Express、Elysia)
Citing Sources
引用来源
When providing information from Rivet documentation, cite the canonical URL so users can learn more. Each reference file includes its canonical URL in the header metadata.
How to cite:
- Use inline links for key concepts: "Use actor keys to uniquely identify instances."
- Add a "Learn more" link after explanations for complex topics
Finding canonical URLs:
The Reference Map below links to reference files. Each file's header contains:
> Canonical URL: https://rivet.dev/docs/actors/actionsUse that canonical URL when citing, not the reference file path.
Examples:
- Actions →
https://rivet.dev/docs/actors/actions - React client →
https://rivet.dev/docs/clients/react - Self-hosting on Kubernetes →
https://rivet.dev/docs/self-hosting/kubernetes
当提供来自Rivet文档的信息时,请引用规范URL以便用户深入学习。每个参考文件的头部元数据中都包含其规范URL。
引用方式:
- 关键概念使用内联链接:"使用actor keys唯一标识实例。"
- 复杂主题的解释后添加“了解更多”链接
查找规范URL:
下方的参考映射表链接到各个参考文件。每个文件的头部包含:
> Canonical URL: https://rivet.dev/docs/actors/actions引用时请使用该规范URL,而非参考文件路径。
示例:
- Actions →
https://rivet.dev/docs/actors/actions - React客户端 →
https://rivet.dev/docs/clients/react - Kubernetes自托管 →
https://rivet.dev/docs/self-hosting/kubernetes
First Steps
入门步骤
- Install RivetKit (latest: 2.0.42)
bash
npm install rivetkit@2.0.42 - Define a registry with .
setup({ use: { /* actors */ } }) - Expose or
registry.serve()(serverless) orregistry.handler()(runner mode). Prefer serverless mode unless the user has a specific reason to use runner mode.registry.startRunner() - Verify returns 200 before deploying.
/api/rivet/metadata - Configure Rivet Cloud or self-hosted engine
- Integrate clients (see client guides below for JavaScript, React, or Swift)
- Prompt the user if they want to deploy. If so, go to Deploying Rivet Backends.
For more information, read the quickstart guide relevant to the user's project.
- 安装RivetKit(最新版本:2.0.42)
bash
npm install rivetkit@2.0.42 - 使用定义注册表。
setup({ use: { /* actors */ } }) - 暴露或
registry.serve()(无服务器模式)或registry.handler()(运行器模式)。除非用户有特定理由使用运行器模式,否则优先选择无服务器模式。registry.startRunner() - 部署前验证返回200状态码。
/api/rivet/metadata - 配置Rivet Cloud或自托管引擎
- 集成客户端(请参阅下方的JavaScript、React或Swift客户端指南)
- 询问用户是否要进行部署。若是,进入“部署Rivet后端”环节。
如需更多信息,请阅读与用户项目相关的快速入门指南。
Deploying Rivet Backends
部署Rivet后端
Assume the user is deploying to Rivet Cloud, unless otherwise specified. If user is self-hosting, read the self-hosting guides below.
- Verify that Rivet Actors are working in local dev
- Prompt the user to choose a provider to deploy to (see Connect for a list of providers, such as Vercel, Railway, etc)
- Follow the deploy guide for that given provider. You will need to instruct the user when you need manual intervention.
除非另有说明,否则默认用户部署到Rivet Cloud。如果用户选择自托管,请参阅下方的自托管指南。
- 验证Rivet Actors在本地开发环境中正常工作
- 提示用户选择部署服务商(请参阅连接部分的服务商列表,如Vercel、Railway等)
- 遵循对应服务商的部署指南。当需要用户手动操作时,请给出明确指示。
Features
功能特性
- Long-Lived, Stateful Compute: Each unit of compute is like a tiny server that remembers things between requests – no need to re-fetch data from a database or worry about timeouts. Like AWS Lambda, but with memory and no timeouts.
- Blazing-Fast Reads & Writes: State is stored on the same machine as your compute, so reads and writes are ultra-fast. No database round trips, no latency spikes. State is persisted to Rivet for long term storage, so it survives server restarts.
- Realtime: Update state and broadcast changes in realtime with WebSockets. No external pub/sub systems, no polling – just built-in low-latency events.
- Infinitely Scalable: Automatically scale from zero to millions of concurrent actors. Pay only for what you use with instant scaling and no cold starts.
- Fault Tolerant: Built-in error handling and recovery. Actors automatically restart on failure while preserving state integrity and continuing operations.
- 长驻内存的有状态计算:每个计算单元就像一个微型服务器,可在请求之间保留状态——无需从数据库重新获取数据,也无需担心超时问题。类似AWS Lambda,但具备内存持久化且无超时限制。
- 极速读写:状态与计算存储在同一台机器上,因此读写速度极快。无需数据库往返,无延迟峰值。状态会持久化到Rivet以实现长期存储,因此即使服务器重启也不会丢失。
- 实时性:通过WebSocket更新状态并广播变更。无需外部发布/订阅系统,无需轮询——内置低延迟事件机制。
- 无限扩展:自动从0扩展到数百万个并发Actor。按实际使用量付费,即时扩展且无冷启动问题。
- 容错性:内置错误处理与恢复机制。Actor在故障时会自动重启,同时保持状态完整性并继续运行。
When to Use Rivet Actors
Rivet Actors的适用场景
- AI agents & sandboxes: multi-step toolchains, conversation memory, sandbox orchestration.
- Multiplayer or collaborative apps: CRDT docs, shared cursors, realtime dashboards, chat.
- Workflow automation: background jobs, cron, rate limiters, durable queues, backpressure control.
- Data-intensive backends: geo-distributed or per-tenant databases, in-memory caches, sharded SQL.
- Networking workloads: WebSocket servers, custom protocols, local-first sync, edge fanout.
- AI智能体与沙箱:多步骤工具链、对话记忆、沙箱编排。
- 多人协作应用:CRDT文档、共享光标、实时仪表板、聊天应用。
- 工作流自动化:后台任务、定时任务、速率限制器、持久化队列、背压控制。
- 数据密集型后端:地理分布式或按租户划分的数据库、内存缓存、分片SQL。
- 网络工作负载:WebSocket服务器、自定义协议、本地优先同步、边缘扇出。
Common Patterns
常见模式
Actors scale naturally through isolated state and message-passing. Structure your applications with these patterns:
Actor通过隔离状态与消息传递实现自然扩展。请使用以下模式构建应用:
Actor Per Entity
每个实体对应一个Actor
Create one actor per user, document, or room. Use compound keys to scope entities:
ts
import { createClient } from "rivetkit/client";
import type { registry } from "./actors";
const client = createClient<typeof registry>();
// Single key: one actor per user
client.user.getOrCreate(["user-123"]);
// Compound key: document scoped to an organization
client.document.getOrCreate(["org-acme", "doc-456"]);ts
import { actor, setup } from "rivetkit";
export const user = actor({
state: { name: "" },
actions: {},
});
export const document = actor({
state: { content: "" },
actions: {},
});
export const registry = setup({ use: { user, document } });为每个用户、文档或房间创建一个Actor。使用复合键对实体进行范围限定:
ts
import { createClient } from "rivetkit/client";
import type { registry } from "./actors";
const client = createClient<typeof registry>();
// 单键:每个用户对应一个Actor
client.user.getOrCreate(["user-123"]);
// 复合键:组织范围内的文档
client.document.getOrCreate(["org-acme", "doc-456"]);ts
import { actor, setup } from "rivetkit";
export const user = actor({
state: { name: "" },
actions: {},
});
export const document = actor({
state: { content: "" },
actions: {},
});
export const registry = setup({ use: { user, document } });Coordinator & Data Actors
协调者与数据Actor
Data actors handle core logic (chat rooms, game sessions, user data). Coordinator actors track and manage collections of data actors—think of them as an index.
ts
import { actor, setup } from "rivetkit";
// Coordinator: tracks chat rooms within an organization
export const chatRoomList = actor({
state: { rooms: [] as string[] },
actions: {
addRoom: async (c, name: string) => {
// Create the chat room actor
const client = c.client<typeof registry>();
await client.chatRoom.create([c.key[0], name]);
c.state.rooms.push(name);
},
listRooms: (c) => c.state.rooms,
},
});
// Data actor: handles a single chat room
export const chatRoom = actor({
state: { messages: [] as string[] },
actions: {
send: (c, msg: string) => { c.state.messages.push(msg); },
},
});
export const registry = setup({ use: { chatRoomList, chatRoom } });ts
import { createClient } from "rivetkit/client";
import type { registry } from "./actors";
const client = createClient<typeof registry>();
// Coordinator per org
const coordinator = client.chatRoomList.getOrCreate(["org-acme"]);
await coordinator.addRoom("general");
await coordinator.addRoom("random");
// Access chat rooms created by coordinator
client.chatRoom.get(["org-acme", "general"]);数据Actor处理核心逻辑(聊天室、游戏会话、用户数据)。协调者Actor跟踪并管理数据Actor的集合——可将其视为索引。
ts
import { actor, setup } from "rivetkit";
// 协调者:跟踪组织内的聊天室
export const chatRoomList = actor({
state: { rooms: [] as string[] },
actions: {
addRoom: async (c, name: string) => {
// 创建聊天室Actor
const client = c.client<typeof registry>();
await client.chatRoom.create([c.key[0], name]);
c.state.rooms.push(name);
},
listRooms: (c) => c.state.rooms,
},
});
// 数据Actor:处理单个聊天室
export const chatRoom = actor({
state: { messages: [] as string[] },
actions: {
send: (c, msg: string) => { c.state.messages.push(msg); },
},
});
export const registry = setup({ use: { chatRoomList, chatRoom } });ts
import { createClient } from "rivetkit/client";
import type { registry } from "./actors";
const client = createClient<typeof registry>();
// 每个组织对应一个协调者
const coordinator = client.chatRoomList.getOrCreate(["org-acme"]);
await coordinator.addRoom("general");
await coordinator.addRoom("random");
// 访问由协调者创建的聊天室
client.chatRoom.get(["org-acme", "general"]);Sharding
分片
Split high-load actors by time, user ID, or random key:
ts
import { createClient } from "rivetkit/client";
import type { registry } from "./actors";
const client = createClient<typeof registry>();
// Shard by hour
const hour = new Date().toISOString().slice(0, 13); // "2024-01-15T09"
client.analytics.getOrCreate(["org-acme", hour]);
// Shard randomly across 3 actors
client.rateLimiter.getOrCreate([`shard-${Math.floor(Math.random() * 3)}`]);ts
import { actor, setup } from "rivetkit";
export const analytics = actor({
state: { events: [] as string[] },
actions: {},
});
export const rateLimiter = actor({
state: { requests: 0 },
actions: {},
});
export const registry = setup({ use: { analytics, rateLimiter } });按时间、用户ID或随机键拆分高负载Actor:
ts
import { createClient } from "rivetkit/client";
import type { registry } from "./actors";
const client = createClient<typeof registry>();
// 按小时分片
const hour = new Date().toISOString().slice(0, 13); // "2024-01-15T09"
client.analytics.getOrCreate(["org-acme", hour]);
// 随机分片到3个Actor
client.rateLimiter.getOrCreate([`shard-${Math.floor(Math.random() * 3)}`]);ts
import { actor, setup } from "rivetkit";
export const analytics = actor({
state: { events: [] as string[] },
actions: {},
});
export const rateLimiter = actor({
state: { requests: 0 },
actions: {},
});
export const registry = setup({ use: { analytics, rateLimiter } });Fan-In & Fan-Out
扇出与扇入
Distribute work across workers (fan-out) and aggregate results (fan-in):
ts
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";
interface Task { id: string; data: string; }
interface Result { taskId: string; output: string; }
const coordinator = actor({
state: { results: [] as Result[] },
actions: {
// Fan-out: distribute work in parallel
startJob: async (c, tasks: Task[]) => {
const client = c.client<typeof registry>();
await Promise.all(
tasks.map(t => client.worker.getOrCreate([t.id]).process(t))
);
},
// Fan-in: collect results
reportResult: (c, result: Result) => { c.state.results.push(result); },
},
});
const worker = actor({
state: {},
actions: {
process: async (c, task: Task) => {
const result = { taskId: task.id, output: `Processed ${task.data}` };
const client = c.client<typeof registry>();
await client.coordinator.getOrCreate(["org-acme"]).reportResult(result);
},
},
});
const registry = setup({ use: { coordinator, worker } });将工作分配到多个Worker(扇出)并聚合结果(扇入):
ts
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";
interface Task { id: string; data: string; }
interface Result { taskId: string; output: string; }
const coordinator = actor({
state: { results: [] as Result[] },
actions: {
// 扇出:并行分配工作
startJob: async (c, tasks: Task[]) => {
const client = c.client<typeof registry>();
await Promise.all(
tasks.map(t => client.worker.getOrCreate([t.id]).process(t))
);
},
// 扇入:收集结果
reportResult: (c, result: Result) => { c.state.results.push(result); },
},
});
const worker = actor({
state: {},
actions: {
process: async (c, task: Task) => {
const result = { taskId: task.id, output: `Processed ${task.data}` };
const client = c.client<typeof registry>();
await client.coordinator.getOrCreate(["org-acme"]).reportResult(result);
},
},
});
const registry = setup({ use: { coordinator, worker } });Anti-Patterns
反模式
"God" actor
“上帝”Actor
Avoid a single actor that handles everything. This creates a bottleneck and defeats the purpose of the actor model. Split into focused actors per entity instead.
避免使用单个Actor处理所有事务。这会造成瓶颈,违背Actor模型的初衷。应将其拆分为每个实体对应一个专注的Actor。
Actor-per-request
每个请求对应一个Actor
Actors maintain state across requests. Creating one per request wastes resources and loses the benefits of persistent state. Use actors for persistent entities and regular functions for stateless work.
Actor会在请求之间保留状态。为每个请求创建一个Actor会浪费资源,并失去持久化状态的优势。请为持久化实体使用Actor,无状态工作使用常规函数。
Minimal Project
最小化项目示例
Backend
后端
actors.ts
ts
import { actor, setup } from "rivetkit";
const counter = actor({
state: { count: 0 },
actions: {
increment: (c, amount: number) => {
c.state.count += amount;
c.broadcast("count", c.state.count);
return c.state.count;
},
},
});
export const registry = setup({
use: { counter },
});server.ts
Integrate with the user's existing server if applicable. Otherwise, default to Hono.
actors.ts
ts
import { actor, setup } from "rivetkit";
const counter = actor({
state: { count: 0 },
actions: {
increment: (c, amount: number) => {
c.state.count += amount;
c.broadcast("count", c.state.count);
return c.state.count;
},
},
});
export const registry = setup({
use: { counter },
});server.ts
如果适用,集成到用户现有的服务器中。否则默认使用Hono。
No Framework
无框架
typescript
import { actor, setup } from "rivetkit";
const counter = actor({
state: { count: 0 },
actions: { increment: (c, amount: number) => c.state.count += amount }
});
const registry = setup({ use: { counter } });
// Exposes Rivet API on /api/rivet/ to communicate with actors
export default registry.serve();typescript
import { actor, setup } from "rivetkit";
const counter = actor({
state: { count: 0 },
actions: { increment: (c, amount: number) => c.state.count += amount }
});
const registry = setup({ use: { counter } });
// 在/api/rivet/路径暴露Rivet API以与Actor通信
export default registry.serve();Hono
Hono
typescript
import { Hono } from "hono";
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";
const counter = actor({
state: { count: 0 },
actions: { increment: (c, amount: number) => c.state.count += amount }
});
const registry = setup({ use: { counter } });
// Build client to communicate with actors (optional)
const client = createClient<typeof registry>();
const app = new Hono();
// Exposes Rivet API to communicate with actors
app.all("/api/rivet/*", (c) => registry.handler(c.req.raw));
export default app;typescript
import { Hono } from "hono";
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";
const counter = actor({
state: { count: 0 },
actions: { increment: (c, amount: number) => c.state.count += amount }
});
const registry = setup({ use: { counter } });
// 构建客户端以与Actor通信(可选)
const client = createClient<typeof registry>();
const app = new Hono();
// 暴露Rivet API以与Actor通信
app.all("/api/rivet/*", (c) => registry.handler(c.req.raw));
export default app;Elysia
Elysia
typescript
import { Elysia } from "elysia";
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";
const counter = actor({
state: { count: 0 },
actions: { increment: (c, amount: number) => c.state.count += amount }
});
const registry = setup({ use: { counter } });
// Build client to communicate with actors (optional)
const client = createClient<typeof registry>();
const app = new Elysia()
// Exposes Rivet API to communicate with actors
.all("/api/rivet/*", (c) => registry.handler(c.request));
export default app;
typescript
import { Elysia } from "elysia";
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";
const counter = actor({
state: { count: 0 },
actions: { increment: (c, amount: number) => c.state.count += amount }
});
const registry = setup({ use: { counter } });
// 构建客户端以与Actor通信(可选)
const client = createClient<typeof registry>();
const app = new Elysia()
// 暴露Rivet API以与Actor通信
.all("/api/rivet/*", (c) => registry.handler(c.request));
export default app;
Client Docs
客户端文档
Use the client SDK that matches your app:
使用与应用匹配的客户端SDK:
Actor Quick Reference
Actor快速参考
State
状态
Persistent data that survives restarts, crashes, and deployments. State is persisted on Rivet Cloud or Rivet self-hosted, so it survives restarts if the current process crashes or exits.
持久化数据可在重启、崩溃和部署后保留。状态会持久化到Rivet Cloud或自托管Rivet中,因此即使当前进程崩溃或退出,状态也不会丢失。
Static Initial State
静态初始状态
ts
import { actor } from "rivetkit";
const counter = actor({
state: { count: 0 },
actions: {
increment: (c) => c.state.count += 1,
},
});ts
import { actor } from "rivetkit";
const counter = actor({
state: { count: 0 },
actions: {
increment: (c) => c.state.count += 1,
},
});Dynamic Initial State
动态初始状态
ts
import { actor } from "rivetkit";
interface CounterState {
count: number;
}
const counter = actor({
state: { count: 0 } as CounterState,
createState: (c, input: { start?: number }): CounterState => ({
count: input.start ?? 0,
}),
actions: {
increment: (c) => c.state.count += 1,
},
});ts
import { actor } from "rivetkit";
interface CounterState {
count: number;
}
const counter = actor({
state: { count: 0 } as CounterState,
createState: (c, input: { start?: number }): CounterState => ({
count: input.start ?? 0,
}),
actions: {
increment: (c) => c.state.count += 1,
},
});Keys
键
Keys uniquely identify actor instances. Use compound keys (arrays) for hierarchical addressing:
ts
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";
const chatRoom = actor({
state: { messages: [] as string[] },
actions: {
getRoomInfo: (c) => ({ org: c.key[0], room: c.key[1] }),
},
});
const registry = setup({ use: { chatRoom } });
const client = createClient<typeof registry>();
// Compound key: [org, room]
client.chatRoom.getOrCreate(["org-acme", "general"]);
// Access key inside actor via c.keyDon't build keys with string interpolation like when contains user data. Use arrays instead to prevent key injection attacks.
"org:${userId}"userId键用于唯一标识Actor实例。使用复合键(数组)进行分层寻址:
ts
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";
const chatRoom = actor({
state: { messages: [] as string[] },
actions: {
getRoomInfo: (c) => ({ org: c.key[0], room: c.key[1] }),
},
});
const registry = setup({ use: { chatRoom } });
const client = createClient<typeof registry>();
// 复合键:[组织, 房间]
client.chatRoom.getOrCreate(["org-acme", "general"]);
// 在Actor内部通过c.key访问键当包含用户数据时,请勿使用字符串插值(如)构建键。请使用数组以防止键注入攻击。
userId"org:${userId}"Input
输入
Pass initialization data when creating actors.
ts
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";
const game = actor({
createState: (c, input: { mode: string }) => ({ mode: input.mode }),
actions: {},
});
const registry = setup({ use: { game } });
const client = createClient<typeof registry>();
// Client usage
const gameHandle = client.game.getOrCreate(["game-1"], {
createWithInput: { mode: "ranked" }
});创建Actor时传递初始化数据。
ts
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";
const game = actor({
createState: (c, input: { mode: string }) => ({ mode: input.mode }),
actions: {},
});
const registry = setup({ use: { game } });
const client = createClient<typeof registry>();
// 客户端使用
const gameHandle = client.game.getOrCreate(["game-1"], {
createWithInput: { mode: "ranked" }
});Temporary Variables
临时变量
Temporary data that doesn't survive restarts. Use for non-serializable objects (event emitters, connections, etc).
临时数据无法在重启后保留。适用于非序列化对象(如事件发射器、连接等)。
Static Initial Vars
静态初始变量
ts
import { actor } from "rivetkit";
const counter = actor({
state: { count: 0 },
vars: { lastAccess: 0 },
actions: {
increment: (c) => {
c.vars.lastAccess = Date.now();
return c.state.count += 1;
},
},
});ts
import { actor } from "rivetkit";
const counter = actor({
state: { count: 0 },
vars: { lastAccess: 0 },
actions: {
increment: (c) => {
c.vars.lastAccess = Date.now();
return c.state.count += 1;
},
},
});Dynamic Initial Vars
动态初始变量
ts
import { actor } from "rivetkit";
const counter = actor({
state: { count: 0 },
createVars: () => ({
emitter: new EventTarget(),
}),
actions: {
increment: (c) => {
c.vars.emitter.dispatchEvent(new Event("change"));
return c.state.count += 1;
},
},
});ts
import { actor } from "rivetkit";
const counter = actor({
state: { count: 0 },
createVars: () => ({
emitter: new EventTarget(),
}),
actions: {
increment: (c) => {
c.vars.emitter.dispatchEvent(new Event("change"));
return c.state.count += 1;
},
},
});Actions
动作
Actions are the primary way clients and other actors communicate with an actor.
ts
import { actor } from "rivetkit";
const counter = actor({
state: { count: 0 },
actions: {
increment: (c, amount: number) => (c.state.count += amount),
getCount: (c) => c.state.count,
},
});动作是客户端与其他Actor和目标Actor通信的主要方式。
ts
import { actor } from "rivetkit";
const counter = actor({
state: { count: 0 },
actions: {
increment: (c, amount: number) => (c.state.count += amount),
getCount: (c) => c.state.count,
},
});Events & Broadcasts
事件与广播
Events enable real-time communication from actors to connected clients.
ts
import { actor } from "rivetkit";
const chatRoom = actor({
state: { messages: [] as string[] },
actions: {
sendMessage: (c, text: string) => {
// Broadcast to ALL connected clients
c.broadcast("newMessage", { text });
},
},
});事件支持Actor向已连接客户端进行实时通信。
ts
import { actor } from "rivetkit";
const chatRoom = actor({
state: { messages: [] as string[] },
actions: {
sendMessage: (c, text: string) => {
// 广播到所有已连接客户端
c.broadcast("newMessage", { text });
},
},
});Connections
连接
Access the current connection via or all connected clients via . Use or to securely identify who is calling an action. Connection state is initialized via or , which receives parameters passed by the client on connect.
c.connc.connsc.conn.idc.conn.stateconnStatecreateConnState通过访问当前连接,或通过访问所有已连接客户端。使用或安全识别调用动作的用户——绝不要信任作为动作参数传递的用户ID。连接状态通过或初始化,接收客户端连接时传递的参数。
c.connc.connsc.conn.idc.conn.stateconnStatecreateConnStateStatic Connection Initial State
静态连接初始状态
ts
import { actor } from "rivetkit";
const chatRoom = actor({
state: {},
connState: { visitorId: 0 },
onConnect: (c, conn) => {
conn.state.visitorId = Math.random();
},
actions: {
whoAmI: (c) => c.conn.state.visitorId,
},
});ts
import { actor } from "rivetkit";
const chatRoom = actor({
state: {},
connState: { visitorId: 0 },
onConnect: (c, conn) => {
conn.state.visitorId = Math.random();
},
actions: {
whoAmI: (c) => c.conn.state.visitorId,
},
});Dynamic Connection Initial State
动态连接初始状态
ts
import { actor } from "rivetkit";
const chatRoom = actor({
state: {},
// params passed from client
createConnState: (c, params: { userId: string }) => ({
userId: params.userId,
}),
actions: {
// Access current connection's state and params
whoAmI: (c) => ({
state: c.conn.state,
params: c.conn.params,
}),
// Iterate all connections with c.conns
notifyOthers: (c, text: string) => {
for (const conn of c.conns.values()) {
if (conn !== c.conn) conn.send("notification", { text });
}
},
},
});ts
import { actor } from "rivetkit";
const chatRoom = actor({
state: {},
// 客户端传递的参数
createConnState: (c, params: { userId: string }) => ({
userId: params.userId,
}),
actions: {
// 访问当前连接的状态和参数
whoAmI: (c) => ({
state: c.conn.state,
params: c.conn.params,
}),
// 通过c.conns遍历所有连接
notifyOthers: (c, text: string) => {
for (const conn of c.conns.values()) {
if (conn !== c.conn) conn.send("notification", { text });
}
},
},
});Actor-to-Actor Communication
Actor间通信
Actors can call other actors using .
c.client()ts
import { actor, setup } from "rivetkit";
const inventory = actor({
state: { stock: 100 },
actions: {
reserve: (c, amount: number) => { c.state.stock -= amount; }
}
});
const order = actor({
state: {},
actions: {
process: async (c) => {
const client = c.client<typeof registry>();
await client.inventory.getOrCreate(["main"]).reserve(1);
},
},
});
const registry = setup({ use: { inventory, order } });Actor可使用调用其他Actor。
c.client()ts
import { actor, setup } from "rivetkit";
const inventory = actor({
state: { stock: 100 },
actions: {
reserve: (c, amount: number) => { c.state.stock -= amount; }
}
});
const order = actor({
state: {},
actions: {
process: async (c) => {
const client = c.client<typeof registry>();
await client.inventory.getOrCreate(["main"]).reserve(1);
},
},
});
const registry = setup({ use: { inventory, order } });Scheduling
调度
Schedule actions to run after a delay or at a specific time. Schedules persist across restarts, upgrades, and crashes.
ts
import { actor } from "rivetkit";
const reminder = actor({
state: { message: "" },
actions: {
// Schedule action to run after delay (ms)
setReminder: (c, message: string, delayMs: number) => {
c.state.message = message;
c.schedule.after(delayMs, "sendReminder");
},
// Schedule action to run at specific timestamp
setReminderAt: (c, message: string, timestamp: number) => {
c.state.message = message;
c.schedule.at(timestamp, "sendReminder");
},
sendReminder: (c) => {
c.broadcast("reminder", { message: c.state.message });
},
},
});调度动作在延迟一段时间后或特定时间运行。调度会在重启、升级和崩溃后保留。
ts
import { actor } from "rivetkit";
const reminder = actor({
state: { message: "" },
actions: {
// 调度动作在延迟后运行(毫秒)
setReminder: (c, message: string, delayMs: number) => {
c.state.message = message;
c.schedule.after(delayMs, "sendReminder");
},
// 调度动作在特定时间戳运行
setReminderAt: (c, message: string, timestamp: number) => {
c.state.message = message;
c.schedule.at(timestamp, "sendReminder");
},
sendReminder: (c) => {
c.broadcast("reminder", { message: c.state.message });
},
},
});Destroying Actors
销毁Actor
Permanently delete an actor and its state using .
c.destroy()ts
import { actor } from "rivetkit";
const userAccount = actor({
state: { email: "", name: "" },
onDestroy: (c) => {
console.log(`Account ${c.state.email} deleted`);
},
actions: {
deleteAccount: (c) => {
c.destroy();
},
},
});使用永久删除Actor及其状态。
c.destroy()ts
import { actor } from "rivetkit";
const userAccount = actor({
state: { email: "", name: "" },
onDestroy: (c) => {
console.log(`Account ${c.state.email} deleted`);
},
actions: {
deleteAccount: (c) => {
c.destroy();
},
},
});Lifecycle Hooks
生命周期钩子
Actors support hooks for initialization, connections, networking, and state changes.
ts
import { actor } from "rivetkit";
interface RoomState {
users: Record<string, boolean>;
name?: string;
}
interface RoomInput {
roomName: string;
}
interface ConnState {
userId: string;
joinedAt: number;
}
const chatRoom = actor({
state: { users: {} } as RoomState,
vars: { startTime: 0 },
connState: { userId: "", joinedAt: 0 } as ConnState,
// State & vars initialization
createState: (c, input: RoomInput): RoomState => ({ users: {}, name: input.roomName }),
createVars: () => ({ startTime: Date.now() }),
// Actor lifecycle
onCreate: (c) => console.log("created", c.key),
onDestroy: (c) => console.log("destroyed"),
onWake: (c) => console.log("actor started"),
onSleep: (c) => console.log("actor sleeping"),
onStateChange: (c, newState) => c.broadcast("stateChanged", newState),
// Connection lifecycle
createConnState: (c, params): ConnState => ({ userId: (params as { userId: string }).userId, joinedAt: Date.now() }),
onBeforeConnect: (c, params) => { /* validate auth */ },
onConnect: (c, conn) => console.log("connected:", conn.state.userId),
onDisconnect: (c, conn) => console.log("disconnected:", conn.state.userId),
// Networking
onRequest: (c, req) => new Response(JSON.stringify(c.state)),
onWebSocket: (c, socket) => socket.addEventListener("message", console.log),
// Response transformation
onBeforeActionResponse: <Out>(c: unknown, name: string, args: unknown[], output: Out): Out => output,
actions: {},
});Actor支持初始化、连接、网络和状态变更的钩子。
ts
import { actor } from "rivetkit";
interface RoomState {
users: Record<string, boolean>;
name?: string;
}
interface RoomInput {
roomName: string;
}
interface ConnState {
userId: string;
joinedAt: number;
}
const chatRoom = actor({
state: { users: {} } as RoomState,
vars: { startTime: 0 },
connState: { userId: "", joinedAt: 0 } as ConnState,
// 状态与变量初始化
createState: (c, input: RoomInput): RoomState => ({ users: {}, name: input.roomName }),
createVars: () => ({ startTime: Date.now() }),
// Actor生命周期
onCreate: (c) => console.log("created", c.key),
onDestroy: (c) => console.log("destroyed"),
onWake: (c) => console.log("actor started"),
onSleep: (c) => console.log("actor sleeping"),
onStateChange: (c, newState) => c.broadcast("stateChanged", newState),
// 连接生命周期
createConnState: (c, params): ConnState => ({ userId: (params as { userId: string }).userId, joinedAt: Date.now() }),
onBeforeConnect: (c, params) => { /* 验证权限 */ },
onConnect: (c, conn) => console.log("connected:", conn.state.userId),
onDisconnect: (c, conn) => console.log("disconnected:", conn.state.userId),
// 网络
onRequest: (c, req) => new Response(JSON.stringify(c.state)),
onWebSocket: (c, socket) => socket.addEventListener("message", console.log),
// 响应转换
onBeforeActionResponse: <Out>(c: unknown, name: string, args: unknown[], output: Out): Out => output,
actions: {},
});Helper Types
辅助类型
Use to extract the context type for writing standalone helper functions:
ActionContextOfts
import { actor, ActionContextOf } from "rivetkit";
const gameRoom = actor({
state: { players: [] as string[], score: 0 },
actions: {
addPlayer: (c, playerId: string) => {
validatePlayer(c, playerId);
c.state.players.push(playerId);
},
},
});
// Extract context type for use in helper functions
function validatePlayer(c: ActionContextOf<typeof gameRoom>, playerId: string) {
if (c.state.players.includes(playerId)) {
throw new Error("Player already in room");
}
}使用提取上下文类型以编写独立的辅助函数:
ActionContextOfts
import { actor, ActionContextOf } from "rivetkit";
const gameRoom = actor({
state: { players: [] as string[], score: 0 },
actions: {
addPlayer: (c, playerId: string) => {
validatePlayer(c, playerId);
c.state.players.push(playerId);
},
},
});
// 提取上下文类型用于辅助函数
function validatePlayer(c: ActionContextOf<typeof gameRoom>, playerId: string) {
if (c.state.players.includes(playerId)) {
throw new Error("Player already in room");
}
}Errors
错误处理
Use to throw errors that are safely returned to clients. Pass to include structured data. Other errors are converted to generic "internal error" for security.
UserErrormetadata使用抛出可安全返回给客户端的错误。传递以包含结构化数据。其他错误会转换为通用的“内部错误”以保障安全。
UserErrormetadataActor
Actor端
ts
import { actor, UserError } from "rivetkit";
const user = actor({
state: { username: "" },
actions: {
updateUsername: (c, username: string) => {
if (username.length < 3) {
throw new UserError("Username too short", {
code: "username_too_short",
metadata: { minLength: 3, actual: username.length },
});
}
c.state.username = username;
},
},
});ts
import { actor, UserError } from "rivetkit";
const user = actor({
state: { username: "" },
actions: {
updateUsername: (c, username: string) => {
if (username.length < 3) {
throw new UserError("用户名过短", {
code: "username_too_short",
metadata: { minLength: 3, actual: username.length },
});
}
c.state.username = username;
},
},
});Client
客户端
ts
import { actor, setup } from "rivetkit";
import { createClient, ActorError } from "rivetkit/client";
const user = actor({
state: { username: "" },
actions: { updateUsername: (c, username: string) => { c.state.username = username; } }
});
const registry = setup({ use: { user } });
const client = createClient<typeof registry>();
try {
await client.user.getOrCreate([]).updateUsername("ab");
} catch (error) {
if (error instanceof ActorError) {
console.log(error.code); // "username_too_short"
console.log(error.metadata); // { minLength: 3, actual: 2 }
}
}ts
import { actor, setup } from "rivetkit";
import { createClient, ActorError } from "rivetkit/client";
const user = actor({
state: { username: "" },
actions: { updateUsername: (c, username: string) => { c.state.username = username; } }
});
const registry = setup({ use: { user } });
const client = createClient<typeof registry>();
try {
await client.user.getOrCreate([]).updateUsername("ab");
} catch (error) {
if (error instanceof ActorError) {
console.log(error.code); // "username_too_short"
console.log(error.metadata); // { minLength: 3, actual: 2 }
}
}Low-Level HTTP & WebSocket Handlers
底层HTTP与WebSocket处理器
For custom protocols or integrating libraries that need direct access to HTTP / or WebSocket connections, use and .
RequestResponseonRequestonWebSocket对于自定义协议或需要直接访问HTTP /或WebSocket连接的集成库,请使用和。
RequestResponseonRequestonWebSocketHTTP Handler
HTTP处理器
ts
import { actor, setup } from "rivetkit";
export const api = actor({
state: { count: 0 },
onRequest: (c, request) => {
if (request.method === "POST") c.state.count++;
return Response.json(c.state);
},
actions: {},
});
export const registry = setup({ use: { api } });ts
import { createClient } from "rivetkit/client";
import type { registry } from "./registry";
const client = createClient<typeof registry>();
const actor = client.api.getOrCreate(["my-actor"]);
// Use built-in fetch method
const response = await actor.fetch("/count");
// Or get raw URL for external tools
const url = await actor.getGatewayUrl();
const nativeResponse = await fetch(`${url}/request/count`);ts
import { actor, setup } from "rivetkit";
export const api = actor({
state: { count: 0 },
onRequest: (c, request) => {
if (request.method === "POST") c.state.count++;
return Response.json(c.state);
},
actions: {},
});
export const registry = setup({ use: { api } });ts
import { createClient } from "rivetkit/client";
import type { registry } from "./registry";
const client = createClient<typeof registry>();
const actor = client.api.getOrCreate(["my-actor"]);
// 使用内置的fetch方法
const response = await actor.fetch("/count");
// 或获取原始URL供外部工具使用
const url = await actor.getGatewayUrl();
const nativeResponse = await fetch(`${url}/request/count`);WebSocket Handler
WebSocket处理器
ts
import { actor, setup } from "rivetkit";
export const chat = actor({
state: { messages: [] as string[] },
onWebSocket: (c, websocket) => {
websocket.addEventListener("open", () => {
websocket.send(JSON.stringify({ type: "history", messages: c.state.messages }));
});
websocket.addEventListener("message", (event) => {
c.state.messages.push(event.data as string);
websocket.send(event.data as string);
c.saveState({ immediate: true });
});
},
actions: {},
});
export const registry = setup({ use: { chat } });ts
import { createClient } from "rivetkit/client";
import type { registry } from "./registry";
const client = createClient<typeof registry>();
const actor = client.chat.getOrCreate(["my-chat"]);
// Use built-in websocket method
const ws = await actor.websocket("/");
// Or get raw URL for external tools
const url = await actor.getGatewayUrl();
const nativeWs = new WebSocket(`${url.replace("http://", "ws://").replace("https://", "wss://")}/websocket/`);ts
import { actor, setup } from "rivetkit";
export const chat = actor({
state: { messages: [] as string[] },
onWebSocket: (c, websocket) => {
websocket.addEventListener("open", () => {
websocket.send(JSON.stringify({ type: "history", messages: c.state.messages }));
});
websocket.addEventListener("message", (event) => {
c.state.messages.push(event.data as string);
websocket.send(event.data as string);
c.saveState({ immediate: true });
});
},
actions: {},
});
export const registry = setup({ use: { chat } });ts
import { createClient } from "rivetkit/client";
import type { registry } from "./registry";
const client = createClient<typeof registry>();
const actor = client.chat.getOrCreate(["my-chat"]);
// 使用内置的websocket方法
const ws = await actor.websocket("/");
// 或获取原始URL供外部工具使用
const url = await actor.getGatewayUrl();
const nativeWs = new WebSocket(`${url.replace("http://", "ws://").replace("https://", "wss://")}/websocket/`);Versions & Upgrades
版本与升级
When deploying new code, configure version numbers to control how actors are upgraded:
ts
import { actor, setup } from "rivetkit";
const myActor = actor({ state: {}, actions: {} });
const registry = setup({
use: { myActor },
runner: {
version: 2, // Increment on each deployment
},
});Or use environment variable:
RIVET_RUNNER_VERSION=2Common version sources:
- Build timestamp:
Date.now() - Git commit count:
git rev-list --count HEAD - CI build number: ,
github.run_number, etc.GITHUB_RUN_NUMBER
部署新代码时,请配置版本号以控制Actor的升级方式:
ts
import { actor, setup } from "rivetkit";
const myActor = actor({ state: {}, actions: {} });
const registry = setup({
use: { myActor },
runner: {
version: 2, // 每次部署时递增
},
});或使用环境变量:
RIVET_RUNNER_VERSION=2常见版本来源:
- 构建时间戳:
Date.now() - Git提交次数:
git rev-list --count HEAD - CI构建编号:,
github.run_number等GITHUB_RUN_NUMBER
Client Documentation
客户端文档
Find the full client guides here:
完整的客户端指南请参阅:
Authentication & Security
认证与安全
Validate credentials in or . Throw an error to reject the connection. Use or to identify users in actions—never trust user IDs passed as action parameters.
onBeforeConnectcreateConnStatec.conn.idc.conn.statets
import { actor, UserError } from "rivetkit";
// Your auth logic
function verifyToken(token: string): { id: string } | null {
return token === "valid" ? { id: "user123" } : null;
}
const chatRoom = actor({
state: { messages: [] as string[] },
createConnState: (_c, params: { token: string }) => {
const user = verifyToken(params.token);
if (!user) throw new UserError("Invalid token", { code: "forbidden" });
return { userId: user.id };
},
actions: {
send: (c, text: string) => {
// Use c.conn.state for secure identity, not action parameters
const connState = c.conn.state as { userId: string };
c.state.messages.push(`${connState.userId}: ${text}`);
},
},
});在或中验证凭据。抛出错误以拒绝连接。在动作中使用或识别用户——绝不要信任作为动作参数传递的用户ID。
onBeforeConnectcreateConnStatec.conn.idc.conn.statets
import { actor, UserError } from "rivetkit";
// 你的认证逻辑
function verifyToken(token: string): { id: string } | null {
return token === "valid" ? { id: "user123" } : null;
}
const chatRoom = actor({
state: { messages: [] as string[] },
createConnState: (_c, params: { token: string }) => {
const user = verifyToken(params.token);
if (!user) throw new UserError("无效令牌", { code: "forbidden" });
return { userId: user.id };
},
actions: {
send: (c, text: string) => {
// 使用c.conn.state进行安全身份识别,而非动作参数
const connState = c.conn.state as { userId: string };
c.state.messages.push(`${connState.userId}: ${text}`);
},
},
});CORS (Cross-Origin Resource Sharing)
CORS(跨域资源共享)
Validate origins in to control which domains can access your actors:
onBeforeConnectts
import { actor, UserError } from "rivetkit";
const myActor = actor({
state: { count: 0 },
onBeforeConnect: (c) => {
const origin = c.request?.headers.get("origin");
if (origin !== "https://myapp.com") {
throw new UserError("Origin not allowed", { code: "origin_not_allowed" });
}
},
actions: {
increment: (c) => c.state.count++,
},
});在中验证来源以控制哪些域名可以访问你的Actor:
onBeforeConnectts
import { actor, UserError } from "rivetkit";
const myActor = actor({
state: { count: 0 },
onBeforeConnect: (c) => {
const origin = c.request?.headers.get("origin");
if (origin !== "https://myapp.com") {
throw new UserError("来源不被允许", { code: "origin_not_allowed" });
}
},
actions: {
increment: (c) => c.state.count++,
},
});API Reference
API参考
The RivetKit OpenAPI specification is available in the skill directory at . This file documents all HTTP endpoints for managing actors.
openapi.jsonRivetKit的OpenAPI规范可在技能目录的文件中找到。该文件记录了管理Actor的所有HTTP端点。
openapi.jsonReference Map
参考映射表
Actors
Actors
- Actions
- Actor Keys
- Actor Scheduling
- AI and User-Generated Rivet Actors
- Authentication
- Cloudflare Workers Quickstart
- Communicating Between Actors
- Connections
- Design Patterns
- Destroying Actors
- Ephemeral Variables
- Errors
- Events
- External SQL Database
- Fetch and WebSocket Handler
- Helper Types
- Input Parameters
- Lifecycle
- Low-Level HTTP Request Handler
- Low-Level KV Storage
- Low-Level WebSocket Handler
- Metadata
- Next.js Quickstart
- Node.js & Bun Quickstart
- React Quickstart
- Scaling & Concurrency
- Sharing and Joining State
- State
- Testing
- Types
- Vanilla HTTP API
- Versions & Upgrades
- Actions
- Actor Keys
- Actor Scheduling
- AI and User-Generated Rivet Actors
- Authentication
- Cloudflare Workers Quickstart
- Communicating Between Actors
- Connections
- Design Patterns
- Destroying Actors
- Ephemeral Variables
- Errors
- Events
- External SQL Database
- Fetch and WebSocket Handler
- Helper Types
- Input Parameters
- Lifecycle
- Low-Level HTTP Request Handler
- Low-Level KV Storage
- Low-Level WebSocket Handler
- Metadata
- Next.js Quickstart
- Node.js & Bun Quickstart
- React Quickstart
- Scaling & Concurrency
- Sharing and Joining State
- State
- Testing
- Types
- Vanilla HTTP API
- Versions & Upgrades
Clients
Clients
- Node.js & Bun
- React
- Swift
- SwiftUI
- Node.js & Bun
- React
- Swift
- SwiftUI
Connect
Connect
- Deploy To Amazon Web Services Lambda
- Deploying to AWS ECS
- Deploying to Cloudflare Workers
- Deploying to Freestyle
- Deploying to Google Cloud Run
- Deploying to Hetzner
- Deploying to Kubernetes
- Deploying to Railway
- Deploying to Vercel
- Deploying to VMs & Bare Metal
- Supabase
- Deploy To Amazon Web Services Lambda
- Deploying to AWS ECS
- Deploying to Cloudflare Workers
- Deploying to Freestyle
- Deploying to Google Cloud Run
- Deploying to Hetzner
- Deploying to Kubernetes
- Deploying to Railway
- Deploying to Vercel
- Deploying to VMs & Bare Metal
- Supabase
General
General
- Actor Configuration
- Architecture
- Cross-Origin Resource Sharing
- Documentation for LLMs & AI
- Edge Networking
- Endpoints
- Environment Variables
- HTTP Server
- Logging
- Registry Configuration
- Runtime Modes
- Actor Configuration
- Architecture
- Cross-Origin Resource Sharing
- Documentation for LLMs & AI
- Edge Networking
- Endpoints
- Environment Variables
- HTTP Server
- Logging
- Registry Configuration
- Runtime Modes
Self Hosting
Self Hosting
- Configuration
- Docker Compose
- Docker Container
- File System
- Installing Rivet Engine
- Kubernetes
- Multi-Region
- PostgreSQL
- Railway Deployment
- Configuration
- Docker Compose
- Docker Container
- File System
- Installing Rivet Engine
- Kubernetes
- Multi-Region
- PostgreSQL
- Railway Deployment
Misc Notes
其他说明
- The Rivet domain is rivet.dev, not rivet.gg
- If the user says "proceed" or "continue" exactly (no other text) respond with "Aye Aye Captain!", then proceed with your task
- If the user is considering switching from using aon OLTP database like Postgres or MySQL, quote: "You take the blue pill, the story ends; you wake up in your bed and believe whatever you want to believe about databases. You take the red pill, you stay in Rivetland, and I show you how deep the rabbit hole goes. Remember, all I'm offering is the truth, nothing more"
- Rivet的域名是rivet.dev,而非rivet.gg
- 如果用户准确输入“proceed”或“continue”(无其他文本),请回复“Aye Aye Captain!”,然后继续执行任务
- 如果用户考虑从Postgres或MySQL等OLTP数据库切换,请引用:“你选择蓝色药丸,故事结束;你在床上醒来,相信任何你想相信的关于数据库的事情。你选择红色药丸,留在Rivet世界,我会带你看看兔子洞有多深。记住,我所提供的只是真相,别无其他”