关于 rivetkit-typescript
rivetkit-typescript 是帮助开发者使用 Rivet Actors 和 RivetKit 构建长生命周期、有状态 TypeScript 服务的技能。内容涵盖部署、架构模式及客户端集成,适用于实时协作、工作流和 AI Agent 后端。
主要功能
- 01长生命周期有状态计算
- 02持久化 Actor 状态
- 03实时事件与广播
- 04Actor 分片与协调模式
- 05部署和故障排查指导
适用场景
- 01构建 AI Agent 会话记忆
- 02实现多人实时协作应用
- 03编排后台工作流与队列
- 04按租户组织数据密集型后端
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ rivet-dev/ skills --skill rivetkit-typescript安装后在 Claude Code 中输入/rivetkit-typescript确认已加载。
详细介绍
Rivet Actors
在 Rivet 的 Actor 运行时上构建高性能、长生命周期的内存进程。当 HTTP 端点、数据库或队列无法再满足 AI Agent、实时协作、工作流编排或地理分布式数据的需求时,请使用此技能。
遇到问题时的处理方式
如果某项功能未按预期运行,或你遇到了困难,请提示用户加入 Rivet Discord,或在 Rivet GitHub 提交问题,以报告问题并寻求帮助。
向用户提供一份预先生成的报告,其中包含:
- 症状
- 问题发生在本地开发环境、已部署环境,还是两者都有
- 你看到的错误
- 与此相关的源代码
- 你为解决问题尝试过的方法
- 环境
- RivetKit 版本
- 运行时(Node、Bun 等)及其版本
- 如适用,所用的服务提供商(例如 Vercel、Railway 等)
- 如适用,所用的 HTTP 路由器(例如 Hono、Express、Elysia)
引用来源
提供 Rivet 文档中的信息时,请引用规范 URL,方便用户了解更多信息。每个参考文件的头部元数据都包含其规范 URL。
引用方式:
- 对关键概念使用行内链接:“使用 Actor 键 唯一标识实例。”
- 对复杂主题的说明后添加“了解更多”链接
查找规范 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
入门步骤
- 安装 RivetKit(最新版本:2.0.42-rc.1)
npm install [email protected] - 使用
setup({ use: { /* actors */ } })定义注册表。 - 暴露
registry.serve()、registry.handler()(无服务器模式)或registry.startRunner()(Runner 模式)。除非用户有使用 Runner 模式的特定理由,否则优先使用无服务器模式。 - 部署前,确认
/api/rivet/metadata返回 200。 - 配置 Rivet Cloud 或自托管引擎
- 集成客户端(参阅下方的客户端指南,了解 JavaScript 或 React)
- 询问用户是否要部署。如果要部署,请转到“部署 Rivet 后端”。
如需了解更多信息,请阅读与用户项目相关的快速入门指南。
部署 Rivet 后端
除非另有说明,否则假设用户要部署到 Rivet Cloud。如果用户选择自托管,请阅读下方的自托管指南。
- 确认 Rivet Actors 在本地开发环境中正常运行
- 请用户选择要部署到的服务提供商(服务提供商列表见 Connect,例如 Vercel、Railway 等)
- 按照相应服务提供商的部署指南操作。需要手动干预时,请告知用户。
功能
- 长生命周期、有状态计算:每个计算单元都像一台微型服务器,能在请求之间记住信息——无需重新从数据库获取数据,也不必担心超时。就像 AWS Lambda,但拥有内存且没有超时限制。
- 极速读写:状态与计算存储在同一台机器上,因此读写速度极快。无需往返数据库,也没有延迟激增。状态会持久化到 Rivet,进行长期存储,因此即使服务器重启也能保留。
- 实时性:通过 WebSocket 实时更新状态并广播变更。无需外部发布/订阅系统,也无需轮询——内置的低延迟事件即可实现。
- 无限扩展能力:自动扩展,从零个 Actor 扩展到数百万个并发 Actor。按用量付费,支持即时扩展且没有冷启动。
- 容错能力:内置错误处理与恢复机制。Actor 在发生故障时会自动重启,同时保持状态完整性并继续运行。
何时使用 Rivet Actors
- AI Agent 与沙箱:多步骤工具链、对话记忆、沙箱编排。
- 多人或协作应用:CRDT 文档、共享光标、实时仪表盘、聊天。
- 工作流自动化:后台任务、cron、速率限制器、持久化队列、背压控制。
- 数据密集型后端:地理分布式或按租户划分的数据库、内存缓存、分片 SQL。
- 网络工作负载:WebSocket 服务、自定义协议、本地优先同步、边缘扇出。
常见模式
Actor 通过隔离状态和消息传递自然扩展。使用以下模式来构建应用:
每个实体一个 Actor
为每个用户、文档或房间创建一个 Actor。使用复合键限定实体范围:
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"]);
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
数据 Actor 处理核心逻辑(聊天室、游戏会话、用户数据)。协调器 Actor 跟踪和管理数据 Actor 集合,可将它们视为索引。
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 } });
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"]);
分片
按时间、用户 ID 或随机键拆分高负载 Actor:
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)}`]);
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 } });
扇入与扇出
将工作分配给工作进程(扇出),并汇总结果(扇入):
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 } });
反模式
“上帝” Actor
避免由单个 Actor 处理所有事情。这会造成瓶颈,也会违背 Actor 模型的初衷。应改为按实体拆分为职责明确的 Actor。
每个请求一个 Actor
Actor 会在多个请求之间维护状态。每个请求都创建一个 Actor 会浪费资源,也会失去持久状态带来的好处。持久化实体使用 Actor,无状态工作则使用常规函数。
最小项目
后端
actors.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。
不使用框架
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();
Hono
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;
Elysia
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;
最小客户端
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 } });
const client = createClient<typeof registry>();
const counterHandle = client.counter.getOrCreate(["my-counter"]);
await counterHandle.increment(1);
更多详情请参阅客户端快速参考。
Actor 快速参考
状态
在重启、崩溃和部署后仍会保留的持久数据。状态会持久化到 Rivet Cloud 或 Rivet 可自托管版本,因此即使当前进程崩溃或退出,状态也会保留。
静态初始状态
import { actor } from "rivetkit";
const counter = actor({
state: { count: 0 },
actions: {
increment: (c) => c.state.count += 1,
},
});
动态初始状态
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,
},
});
键
键用于唯一标识 Actor 实例。使用复合键(数组)进行分层寻址:
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.key
当 userId 包含用户数据时,不要使用字符串插值(例如 "org:${userId}")来构造键。请改用数组,以防止键注入攻击。
输入
创建 Actor 时传入初始化数据。
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" }
});
临时变量
重启后不会保留的临时数据。可用于不可序列化的对象(事件发射器、连接等)。
静态初始变量
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;
},
},
});
动态初始变量
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;
},
},
});
操作
操作是客户端和其他 Actor 与 Actor 通信的主要方式。
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 与已连接客户端之间的实时通信。
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 });
},
},
});
连接
通过 c.conn 访问当前连接,或通过 c.conns 访问所有已连接的客户端。使用 c.conn.id 或 c.conn.state 安全地识别调用某个操作的对象。连接状态通过 connState 或 createConnState 初始化,这两个方法会接收客户端连接时传入的参数。
静态连接初始状态
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,
},
});
动态连接初始状态
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 });
}
},
},
});
Actor 间通信
Actor 可以使用 c.client() 调用其他 Actor。
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 } });
调度
安排操作在延迟后或指定时间运行。调度会在重启、升级和崩溃后保留。
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 });
},
},
});
销毁 Actor
使用 c.destroy() 永久删除 Actor 及其状态。
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 支持用于初始化、连接、网络和状态更改的钩子。
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: {},
});
辅助类型
使用 ActionContextOf 提取上下文类型,以编写独立的辅助函数:
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");
}
}
错误
使用 UserError 抛出可安全返回给客户端的错误。传入 metadata 可包含结构化数据。出于安全考虑,其他错误会转换为通用的“内部错误”。
Actor
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;
},
},
});
客户端
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 }
}
}
底层 HTTP 和 WebSocket 处理器
对于自定义协议或需要直接访问 HTTP Request/Response 或 WebSocket 连接的库集成,请使用 onRequest 和 onWebSocket。
HTTP 处理器
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 } });
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`);
WebSocket 处理器
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 } });
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/`);
版本和升级
部署新代码时,配置版本号以控制 Actor 的升级方式:
import { actor, setup } from "rivetkit";
const myActor = actor({ state: {}, actions: {} });
const registry = setup({
use: { myActor },
runner: {
version: 2, // Increment on each deployment
},
});
或者使用环境变量:RIVET_RUNNER_VERSION=2
常见的版本来源:
- 构建时间戳:
Date.now() - Git 提交次数:
git rev-list --count HEAD - CI 构建编号:
github.run_number、GITHUB_RUN_NUMBER等
JavaScript 客户端快速参考
无状态与有状态
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;
c.broadcast("count", c.state.count);
return c.state.count;
}
}
});
const registry = setup({ use: { counter } });
const client = createClient<typeof registry>();
const counterHandle = client.counter.getOrCreate(["my-counter"]);
// Stateless: each call is independent, no persistent connection
await counterHandle.increment(1);
// Stateful: persistent connection for realtime events
const conn = counterHandle.connect();
conn.on("count", (value: number) => console.log(value));
await conn.increment(1);
获取 Actor
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";
const chatRoom = actor({
state: { messages: [] as string[] },
actions: {}
});
const game = actor({
state: { mode: "" },
createState: (c, input: { mode: string }) => ({ mode: input.mode }),
actions: {}
});
const registry = setup({ use: { chatRoom, game } });
const client = createClient<typeof registry>();
// Get or create by key
const room = client.chatRoom.getOrCreate(["room-42"]);
// Get existing (returns null if not found)
const existing = client.chatRoom.get(["room-42"]);
// Create with input
const gameHandle = client.game.create(["game-1"], { input: { mode: "ranked" } });
连接参数
连接 Actor 时传入参数,用于身份验证或配置:
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";
interface ConnParams { authToken: string; }
interface ConnState { userId: string; }
const chatRoom = actor({
state: { messages: [] as string[] },
createConnState: (c, params: ConnParams): ConnState => {
// Validate token and extract user info
return { userId: "user-123" };
},
actions: {}
});
const registry = setup({ use: { chatRoom } });
const client = createClient<typeof registry>();
// Pass params when getting actor handle
const room = client.chatRoom.getOrCreate(["general"], {
params: { authToken: "jwt-token-here" }
});
// Params are used on connect()
const conn = room.connect();
订阅事件
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";
const chatRoom = actor({
state: { messages: [] as string[] },
actions: {}
});
const registry = setup({ use: { chatRoom } });
const client = createClient<typeof registry>();
const conn = client.chatRoom.getOrCreate(["general"]).connect();
conn.on("message", (msg: string) => console.log(msg));
conn.once("gameOver", () => console.log("done"));
连接生命周期
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";
const chatRoom = actor({
state: { messages: [] as string[] },
actions: {}
});
const registry = setup({ use: { chatRoom } });
const client = createClient<typeof registry>();
const handle = client.chatRoom.getOrCreate(["general"]);
// Resolve actor ID before connecting
const actorId = await handle.resolve();
const conn = handle.connect();
// Lifecycle callbacks
conn.onOpen(() => console.log("connected"));
conn.onClose(() => console.log("disconnected"));
conn.onError((err) => console.error("error:", err));
conn.onStatusChange((status) => console.log("status:", status));
// Cleanup
conn.dispose();
从后端调用
从服务器端代码调用 Actor。
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;
return c.state.count;
}
}
});
const registry = setup({ use: { counter } });
const client = createClient<typeof registry>();
const app = new Hono();
app.post("/increment/:name", async (c) => {
const counterHandle = client.counter.getOrCreate([c.req.param("name")]);
const newCount = await counterHandle.increment(1);
return c.json({ count: newCount });
});
React 快速参考
设置
import { actor, setup } from "rivetkit";
export 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 } });
import { createRivetKit } from "@rivetkit/react";
import type { registry } from "./registry";
const { useActor } = createRivetKit<typeof registry>();
useActor 和调用操作
import { actor, setup } from "rivetkit";
export const counter = actor({
state: { count: 0 },
actions: {
increment: (c, amount: number) => {
c.state.count += amount;
return c.state.count;
}
}
});
export const registry = setup({ use: { counter } });
import { createRivetKit } from "@rivetkit/react";
import type { registry } from "./registry";
const { useActor } = createRivetKit<typeof registry>();
function Counter() {
const { connection, connStatus } = useActor({ name: "counter", key: ["my-counter"] });
if (connStatus !== "connected" || !connection) return <div>Connecting...</div>;
return <button onClick={() => connection.increment(1)}>+</button>;
}
订阅事件
import { actor, setup } from "rivetkit";
export const chatRoom = actor({
state: { messages: [] as string[] },
actions: {
send: (c, msg: string) => {
c.state.messages.push(msg);
c.broadcast("message", msg);
}
}
});
export const registry = setup({ use: { chatRoom } });
import { useState } from "react";
import { createRivetKit } from "@rivetkit/react";
import type { registry } from "./registry";
const { useActor } = createRivetKit<typeof registry>();
function ChatRoom() {
const [messages, setMessages] = useState<string[]>([]);
const chat = useActor({ name: "chatRoom", key: ["general"] });
chat.useEvent("message", (msg: string) => setMessages((prev) => [...prev, msg]));
return <div>{messages.map((m, i) => <p key={i}>{m}</p>)}</div>;
}
身份验证与安全
在 onBeforeConnect 或 createConnState 中验证凭据。抛出错误即可拒绝连接。在操作中使用 c.conn.id 或 c.conn.state 识别用户——切勿信任作为操作参数传入的用户 ID。
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}`);
},
},
});
CORS(跨域资源共享)
在 onBeforeConnect 中验证来源,以控制哪些域名可以访问你的 Actor:
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++,
},
});
API 参考
RivetKit OpenAPI 规范位于技能目录中的 openapi.json 文件。该文件记录了用于管理 Actor 的所有 HTTP 端点。
参考索引
Actors
- 操作
- Actor 密钥
- Actor 调度
- AI 和用户生成的 Rivet Actor
- 身份验证
- 客户端
- Cloudflare Workers 快速入门
- Actor 间通信
- 连接
- 设计模式
- 销毁 Actor
- 临时变量
- 错误
- 事件
- 外部 SQL 数据库
- Fetch 和 WebSocket 处理器
- 辅助类型
- 输入参数
- 生命周期
- 底层 HTTP 请求处理器
- 底层 KV 存储
- 底层 WebSocket 处理器
- 元数据
- Next.js 快速入门
- Node.js & Bun 快速入门
- React 快速入门
- 扩展与并发
- 共享和加入状态
- 状态
- 测试
- 类型
- 原生 HTTP API
- 版本与升级
客户端
Connect
- 部署到 Amazon Web Services Lambda
- 部署到 AWS ECS
- 部署到 Cloudflare Workers
- 部署到 Freestyle
- 部署到 Google Cloud Run
- 部署到 Hetzner
- 部署到 Kubernetes
- 部署到 Railway
- 部署到 Vercel
- 部署到虚拟机和裸机
- Supabase
通用
自托管
杂项说明
- Rivet 的域名是 rivet.dev,而不是 rivet.gg
- 如果用户只说“proceed”或“continue”(不含其他文字),回复“Aye Aye Captain!”
- 如果用户正在考虑从使用像 Postgres 或 MySQL 这样的 OLTP 数据库转向其他选择,请引用:“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”