Acteurs Rivet
Construisez des processus en mémoire haute performance et longue durée sur le runtime d'acteurs de Rivet. Utilisez cette skill chaque fois que les endpoints HTTP, les bases de données ou les files d'attente ne répondent plus aux exigences des agents IA, de la collaboration en temps réel, de l'orchestration de workflows ou des données distribuées géographiquement.
Que Faire Si Vous Êtes Bloqué
Si quelque chose ne fonctionne pas comme prévu ou si vous êtes bloqué, invitez l'utilisateur à rejoindre le Discord Rivet ou à signaler un problème sur GitHub Rivet pour obtenir de l'aide.
Fournissez à l'utilisateur un rapport pré-généré contenant :
- Symptômes
- Si cela se produit en développement local, en production, ou les deux
- L'erreur que vous voyez
- Le code source pertinent associé
- Ce que vous avez essayé pour le résoudre
- Environnement
- Version de RivetKit
- Runtime (Node, Bun, etc.) avec sa version
- Le cas échéant, le fournisseur utilisé (p. ex. Vercel, Railway, etc.)
- Le cas échéant, le routeur HTTP utilisé (p. ex. Hono, Express, Elysia)
Déboguer les Acteurs
Utilisez l'API HTTP de l'inspecteur pour examiner les acteurs en cours d'exécution. Ces endpoints sont accessibles via la passerelle à /gateway/{actor_id}/inspector/*. Endpoints clés :
GET /inspector/summary- snapshot complet de l'acteur (état, connexions, RPCs, file d'attente)GET /inspector/state/PATCH /inspector/state- lire/écrire l'état de l'acteurGET /inspector/connections- connexions activesGET /inspector/rpcs- actions disponiblesPOST /inspector/action/{name}- exécuter une action avec{"args": [...]}POST /inspector/database/execute- exécuter SQL avec{"sql": "...", "args": [...]}ou{"sql": "...", "properties": {...}}pour les lectures ou mutationsGET /inspector/queue?limit=50- statut de la file d'attenteGET /inspector/traces?startMs=0&endMs=...&limit=1000- traces spans (JSON OTLP)GET /inspector/workflow-history- historique et statut du workflow en JSON (nameRegistry,entries,entryMetadata)POST /inspector/workflow/replay- relancer un workflow à partir d'une étape spécifique ou du débutGET /inspector/database/schema- tables et vues SQLite exposées parc.dbGET /inspector/database/rows?table=...&limit=100&offset=0- lignes SQLite paginées pour une table ou vuePOST /inspector/workflow/replay- relancer un workflow à partir d'une étape spécifique ou du début
En développement local, aucun token d'authentification n'est requis. En production, passez Authorization: Bearer <RIVET_INSPECTOR_TOKEN>. Le token d'inspecteur spécifique à l'acteur utilisé par l'interface utilisateur de l'inspecteur autonome est également accepté pour les endpoints d'inspecteur. Voir la documentation de débogage pour plus de détails.
Citer les Sources
Lorsque vous fournissez des informations de la documentation Rivet, citez l'URL canonique pour que les utilisateurs puissent en savoir plus. Chaque fichier de référence inclut son URL canonique dans les métadonnées d'en-tête.
Comment citer :
- Utilisez des liens inline pour les concepts clés : « Utilisez les clés d'acteur pour identifier de manière unique les instances. »
- Ajoutez un lien « En savoir plus » après les explications sur les sujets complexes
Trouver les URLs canoniques :
La carte de référence ci-dessous renvoie vers les fichiers de référence. L'en-tête de chaque fichier contient :
> URL canonique : https://rivet.dev/docs/actors/actions
Utilisez cette URL canonique lors de la citation, pas le chemin du fichier de référence.
Exemples :
- Actions →
https://rivet.dev/docs/actors/actions - Client React →
https://rivet.dev/docs/clients/react - Auto-hébergement sur Kubernetes →
https://rivet.dev/docs/self-hosting/kubernetes
Vérification de Version
Avant de commencer, vérifiez que le projet de l'utilisateur utilise la dernière version de RivetKit (dernière : 2.2.0). Regardez la version rivetkit dans le package.json de l'utilisateur (vérifiez à la fois dependencies et devDependencies). Si la version installée est plus ancienne que 2.2.0, informez l'utilisateur et suggérez une mise à jour :
npm install rivetkit@2.2.0
Si l'utilisateur utilise également @rivetkit/react, @rivetkit/next-js ou d'autres packages client @rivetkit/*, suggérez également leur mise à jour. Les versions obsolètes peuvent contenir des bugs connus ou des fonctionnalités manquantes qui causent des problèmes.
Premières Étapes
- Installez RivetKit (dernière : 2.2.0)
npm install rivetkit@2.2.0 - Définissez un registre avec
setup({ use: { /* acteurs */ } }). - Appelez
registry.start()pour démarrer le serveur. Pour l'intégration d'un serveur HTTP personnalisé, utilisezregistry.handler()avec un routeur comme Hono. Pour les déploiements sans serveur, utilisezregistry.serve(). Pour le mode runner uniquement, utilisezregistry.startRunner(). - Vérifiez que
/api/rivet/metadataretourne 200 avant de déployer. - Configurez Rivet Cloud ou le moteur auto-hébergé
- Vous devez configurer la gestion des versions pour les builds de production. Ceci n'est pas nécessaire pour le développement local. Voir Versions & Upgrades.
- Intégrez les clients (consultez les guides clients ci-dessous pour JavaScript, React ou Swift)
- Demandez à l'utilisateur s'il souhaite déployer. Si oui, allez à Déploiement des Backends Rivet.
Pour plus d'informations, lisez le guide de démarrage rapide pertinent pour le projet de l'utilisateur.
Configuration du Projet
.gitignore
Chaque projet RivetKit doit avoir un .gitignore. Incluez au minimum :
node_modules/
dist/
.env
.dockerignore
Chaque projet avec un Dockerfile doit avoir un .dockerignore pour garder l'image petite et éviter les fuites de secrets :
node_modules/
dist/
.env
.git/
Dockerfile
Utilisez ceci comme Dockerfile de base pour déployer un projet RivetKit. L'argument de build RIVET_RUNNER_VERSION n'est nécessaire que lors de l'auto-hébergement ou de l'utilisation d'un runner personnalisé (non nécessaire pour Rivet Compute). Il permet à Rivet de suivre la version de l'acteur en cours d'exécution et de terminer les anciens acteurs lors du déploiement. Voir https://rivet.dev/docs/actors/versions pour plus de détails.
FROM node:24-alpine
ARG RIVET_RUNNER_VERSION
ENV RIVET_RUNNER_VERSION=$RIVET_RUNNER_VERSION
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build --if-present
CMD ["node", "dist/index.js"]
Construisez avec :
docker build --build-arg RIVET_RUNNER_VERSION=$(date +%s) .
Ajustez la CMD pour correspondre au point d'entrée du projet. Si le projet utilise un répertoire de sortie ou une commande de démarrage différents, mettez à jour en conséquence.
Politique de Gestion des Erreurs
- Préférez un comportement fail-fast par défaut.
- Évitez
try/catchà moins que ce ne soit requis pour un véritable chemin de récupération, une limite de nettoyage ou pour ajouter du contexte exploitable. - Ne supprimez jamais les erreurs. Si vous ajoutez un
catch, vous devez gérer l'erreur explicitement, au minimum en la journalisant. - Quand vous ne pouvez pas récupérer, journalisez le contexte et relancez.
État vs Vars : Règles de Persistance
c.vars est éphémère. Les données dans c.vars sont perdues à chaque redémarrage, crash, mise à jour ou cycle de sommeil/réveil. Utilisez c.vars uniquement pour les objets non sérialisables (p. ex. moteurs de physique, références WebSocket, émetteurs d'événements, caches) ou les données d'exécution véritablement transitoires (p. ex. direction d'entrée actuelle qui n'importe pas après la déconnexion).
Options de stockage persistant. Toute donnée qui doit survivre aux redémarrages doit être dans l'une de celles-ci, PAS dans c.vars :
c.state— données sérialisables CBOR pour les petits ensembles de données limités. Idéal pour la configuration, les compteurs, les petites listes de joueurs, les drapeaux de phase, etc. Restez sous 128 KB. Ne stockez pas de données non limitées ou croissantes ici (p. ex. journaux de chat, historiques d'événements, listes d'entités générées qui se développent sans limite). L'état est lu/écrit en tant que blob unique à chaque cycle de persistance.c.kv— magasin clé-valeur pour les données non limitées. C'est ce quec.stateutilise en dessous. Supporte les valeurs binaires. Utilisez pour les données plus grandes ou de taille variable comme les inventaires utilisateurs, les chunks du monde, les blobs de fichiers ou tout ensemble qui peut se développer avec le temps. Les clés sont scoped à l'instance d'acteur.c.db— base de données SQLite pour les données structurées ou complexes. Utilisez quand vous avez besoin de requêtes, d'index, de jointures, d'agrégations ou de modélisation relationnelle. Idéal pour les classements, les historiques de matchs, les pools de joueurs ou toute donnée qui bénéficie de SQL.
Erreur courante : stocker des données significatives de jeu/application dans c.vars au lieu de les persister. Par exemple, si les utilisateurs peuvent générer des objets dans une simulation de physique, les définitions de génération (position, taille, type) doivent être persistées dans c.state (ou c.kv si non limitées), même si le moteur de physique gère le temps réel (non sérialisable) dans c.vars. Au redémarrage, run() doit recréer les objets runtime à partir des données persistées.
Déploiement des Backends Rivet
Supposez que l'utilisateur déploie sur Rivet Cloud, sauf indication contraire. Si l'utilisateur effectue l'auto-hébergement, consultez les guides d'auto-hébergement ci-dessous.
- Vérifiez que les Acteurs Rivet fonctionnent en développement local
- Invitez l'utilisateur à choisir un fournisseur pour déployer (voir Connect pour une liste des fournisseurs, tels que Vercel, Railway, etc.)
- Suivez le guide de déploiement pour ce fournisseur donné. Vous devrez instruire l'utilisateur quand vous avez besoin d'une intervention manuelle.
Référence API
La spécification OpenAPI de RivetKit est disponible dans le répertoire de la skill à openapi.json. Ce fichier documente tous les endpoints HTTP pour gérer les acteurs.
Notes Diverses
- Le domaine Rivet est rivet.dev, pas rivet.gg
Mise en Garde TypeScript : Inférence de Client Acteur
- Dans les projets TypeScript multi-fichiers, les appels bidirectionnels d'acteurs peuvent créer une dépendance circulaire de type quand les deux acteurs utilisent
c.client<typeof registry>(). - Les symptômes incluent généralement
c.statedevenantunknown, les méthodes d'acteur devenant possiblementundefined, ou des erreursTS2322/TS2722après le premier appel d'acteur croisé. - Si une action retourne le résultat d'un appel d'acteur autre, préférez une annotation de type de retour explicite sur cette action au lieu de vous fier à l'inférence via
c.client<typeof registry>(). - Si les types de retour explicites ne suffisent pas, utilisez un type de client ou de registre plus étroit pour uniquement les acteurs que l'action nécessite.
- En dernier recours, passez
unknownpour le type de registre et soyez explicite que cela abandonne la sécurité des types à ce site d'appel.
Fonctionnalités
- Calcul Longue Durée et Stateful : Chaque unité de calcul est comme un petit serveur qui se souvient des choses entre les demandes – pas besoin de re-récupérer les données d'une base de données ou de se soucier des délais d'expiration. Comme AWS Lambda, mais avec mémoire et sans délais d'expiration.
- Lectures & Écritures Blazing-Fast : L'état est stocké sur la même machine que votre calcul, donc les lectures et écritures sont ultra-rapides. Pas de requêtes de base de données, pas de pics de latence. L'état est persisté à Rivet pour le stockage à long terme, donc il survit aux redémarrages du serveur.
- Temps Réel : Mettez à jour l'état et diffusez les changements en temps réel avec WebSockets. Pas de systèmes pub/sub externes, pas de polling – juste les événements à faible latence intégrés.
- Infiniment Scalable : Mettez à l'échelle automatiquement de zéro à des millions d'acteurs concurrents. Payez uniquement pour ce que vous utilisez avec la mise à l'échelle instantanée et sans démarrages à froid.
- Tolérant aux Pannes : Gestion des erreurs et récupération intégrées. Les acteurs redémarrent automatiquement en cas de défaillance tout en préservant l'intégrité de l'état et en continuant les opérations.
Quand Utiliser les Acteurs Rivet
- Agents IA & sandboxes : chaînes multi-étapes, mémoire de conversation, orchestration de sandbox.
- Apps multiplayers ou collaboratives : docs CRDT, curseurs partagés, tableaux de bord en temps réel, chat.
- Automatisation des workflows : travaux de fond, cron, limiteurs de débit, files d'attente durables, contrôle de la contrepression.
- Backends intensifs en données : bases de données distribuées géographiquement ou par tenant, caches en mémoire, SQL shardé.
- Charges de travail réseau : serveurs WebSocket, protocoles personnalisés, sync local-first, fanout edge.
Projet Minimal
Backend
index.ts
import { actor, event, setup } from "rivetkit";
const counter = actor({
state: { count: 0 },
events: {
count: event<number>(),
},
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 },
});
registry.start();
Docs Client
Utilisez le SDK client qui correspond à votre app :
Référence Rapide Acteur
État En Mémoire
Données persistantes qui survivent aux redémarrages, crashes et déploiements. L'état est persisté sur Rivet Cloud ou Rivet auto-hébergé, donc il survit aux redémarrages si le processus actuel crash ou s'arrête.
État Initial Statique
import { actor } from "rivetkit";
const counter = actor({
state: { count: 0 },
actions: {
increment: (c) => c.state.count += 1,
},
});
État Initial Dynamique
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,
},
});
Clés
Les clés identifient de manière unique les instances d'acteur. Utilisez des clés composées (tableaux) pour l'adressage hiérarchique :
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>("http://localhost:6420");
// Clé composée : [org, room]
client.chatRoom.getOrCreate(["org-acme", "general"]);
// Accédez à la clé dans l'acteur via c.key
Ne construisez pas les clés avec interpolation de chaîne comme "org:${userId}" quand userId contient des données utilisateur. Utilisez plutôt des tableaux pour éviter les attaques par injection de clé.
Input
Passez les données d'initialisation lors de la création d'acteurs. L'input n'est disponible que dans createState et onCreate, alors stockez-le dans l'état si vous en avez besoin plus tard.
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";
const game = actor({
state: { mode: "" },
createState: (c, input: { mode: string }) => ({
mode: input.mode, // Stockez l'input dans l'état pour un accès ultérieur
}),
actions: {
getMode: (c) => c.state.mode,
},
});
const registry = setup({ use: { game } });
const client = createClient<typeof registry>("http://localhost:6420");
// Utilisation du client
const gameHandle = client.game.getOrCreate(["game-1"], {
createWithInput: { mode: "ranked" },
});
Variables Temporaires
Données temporaires qui ne survivent pas aux redémarrages. Utilisez pour les objets non sérialisables (émetteurs d'événements, connexions, etc.).
Vars Initial Statique
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;
},
},
});
Vars Initial Dynamique
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
Les actions sont le principal moyen pour les clients et les autres acteurs de communiquer avec un acteur.
import { actor } from "rivetkit";
const counter = actor({
state: { count: 0 },
actions: {
increment: (c, amount: number) => (c.state.count += amount),
getCount: (c) => c.state.count,
},
});
Événements & Broadcasts
Les événements permettent la communication en temps réel des acteurs aux clients connectés.
import { actor, event } from "rivetkit";
const chatRoom = actor({
state: { messages: [] as string[] },
events: {
newMessage: event<{ text: string }>(),
},
actions: {
sendMessage: (c, text: string) => {
// Diffusez à TOUS les clients connectés
c.broadcast("newMessage", { text });
},
},
});
Connexions
Accédez à la connexion actuelle via c.conn ou à tous les clients connectés via c.conns. Utilisez c.conn.id ou c.conn.state pour identifier de manière sécurisée qui appelle une action. L'état de connexion est initialisé via connState ou createConnState, qui reçoit les paramètres passés par le client à la connexion.
État Initial de Connexion Statique
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,
},
});
État Initial de Connexion Dynamique
import { actor } from "rivetkit";
const chatRoom = actor({
state: {},
// params passés par le client
createConnState: (c, params: { userId: string }) => ({
userId: params.userId,
}),
actions: {
// Accédez à l'état et aux paramètres de la connexion actuelle
whoAmI: (c) => ({
state: c.conn.state,
params: c.conn.params,
}),
// Itérez toutes les connexions avec c.conns
notifyOthers: (c, text: string) => {
for (const conn of c.conns.values()) {
if (conn !== c.conn) conn.send("notification", { text });
}
},
},
});
Files d'Attente
Utilisez les files d'attente pour traiter les messages durables dans l'ordre à l'intérieur d'une boucle run.
import { actor, queue } from "rivetkit";
const counter = actor({
state: { value: 0 },
queues: {
increment: queue<{ amount: number }>(),
},
run: async (c) => {
for await (const message of c.queue.iter()) {
c.state.value += message.body.amount;
}
},
});
Workflows
Utilisez les workflows quand votre logique run a besoin d'une exécution multi-étapes durable et rejouable.
import { actor, queue } from "rivetkit";
import { workflow } from "rivetkit/workflow";
const worker = actor({
state: { processed: 0 },
queues: {
tasks: queue<{ url: string }>(),
},
run: workflow(async (ctx) => {
await ctx.loop("task-loop", async (loopCtx) => {
const message = await loopCtx.queue.next("wait-task");
await loopCtx.step("process-task", async () => {
await processTask(message.body.url);
loopCtx.state.processed += 1;
});
});
}),
});
async function processTask(url: string): Promise<void> {
const res = await fetch(url, { method: "POST" });
if (!res.ok) throw new Error(`Task failed: ${res.status}`);
}
Communication Entre Acteurs
Les acteurs peuvent appeler d'autres acteurs en utilisant c.client().
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 } });
Planification
Planifiez l'exécution d'actions après un délai ou à un moment spécifique. Les planifications persistent à travers les redémarrages, mises à jour et crashes.
import { actor, event } from "rivetkit";
const reminder = actor({
state: { message: "" },
events: {
reminder: event<{ message: string }>(),
},
actions: {
// Planifiez une action pour s'exécuter après un délai (ms)
setReminder: (c, message: string, delayMs: number) => {
c.state.message = message;
c.schedule.after(delayMs, "sendReminder");
},
// Planifiez une action pour s'exécuter à un timestamp spécifique
setReminderAt: (c, message: string, timestamp: number) => {
c.state.message = message;
c.schedule.at(timestamp, "sendReminder");
},
sendReminder: (c) => {
c.broadcast("reminder", { message: c.state.message });
},
},
});
Destruction d'Acteurs
Supprimez définitivement un acteur et son état en utilisant c.destroy().
import { actor } from "rivetkit";
const userAccount = actor({
state: { email: "", name: "" },
onDestroy: (c) => {
console.log(`Account ${c.state.email} deleted`);
},
actions: {
deleteAccount: (c) => {
c.destroy();
},
},
});
Hooks de Cycle de Vie
Les acteurs supportent les hooks pour l'initialisation, le traitement de fond, les connexions, la mise en réseau et les changements d'état. Utilisez run pour les boucles de fond longues et quittez proprement au shutdown avec c.aborted ou c.abortSignal.
import { actor, event, queue } 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,
events: {
stateChanged: event<RoomState>(),
},
queues: {
work: queue<{ task: string }>(),
},
// Initialisation de l'état & vars
createState: (c, input: RoomInput): RoomState => ({
users: {},
name: input.roomName,
}),
createVars: () => ({ startTime: Date.now() }),
// Cycle de vie de l'acteur
onCreate: (c) => console.log("created", c.key),
onDestroy: (c) => console.log("destroyed"),
onWake: (c) => console.log("actor started"),
onSleep: (c) => console.log("actor sleeping"),
run: async (c) => {
for await (const message of c.queue.iter()) {
console.log("processing", message.body.task);
}
},
onStateChange: (c, newState) => c.broadcast("stateChanged", newState),
// Cycle de vie de la connexion
createConnState: (c, params): ConnState => ({
userId: (params as { userId: string }).userId,
joinedAt: Date.now(),
}),
onBeforeConnect: (c, params) => {
/* valider auth */
},
onConnect: (c, conn) => console.log("connected:", conn.state.userId),
onDisconnect: (c, conn) => console.log("disconnected:", conn.state.userId),
// Mise en réseau
onRequest: (c, req) => new Response(JSON.stringify(c.state)),
onWebSocket: (c, socket) => socket.addEventListener("message", console.log),
// Transformation de réponse
onBeforeActionResponse: <Out>(
c: unknown,
name: string,
args: unknown[],
output: Out,
): Out => output,
actions: {},
});
Types de Contexte
Lorsque vous écrivez des fonctions d'aide en dehors de la définition d'acteur, utilisez *ContextOf<typeof myActor> pour extraire le type de contexte correct. Ne définissez pas manuellement votre propre interface de contexte — dérivez-la toujours de la définition d'acteur.
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);
},
},
});
// Bon : dérivez le type de contexte de la définition d'acteur
function validatePlayer(c: ActionContextOf<typeof gameRoom>, playerId: string) {
if (c.state.players.includes(playerId)) {
throw new Error("Player already in room");
}
}
// Mauvais : ne définissez pas manuellement les types de contexte comme ceci
// type MyContext = { state: { players: string[] }; ... };
Erreurs
Utilisez UserError pour lancer des erreurs qui sont retournées en toute sécurité aux clients. Passez metadata pour inclure les données structurées. Les autres erreurs sont converties en « erreur interne » générique pour la sécurité.
Acteur
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;
},
},
});
Client
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>("http://localhost:6420");
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 }
}
}
Handlers HTTP & WebSocket Bas Niveau
Pour les protocoles personnalisés ou l'intégration de bibliothèques qui ont besoin d'accès direct aux connexions Request/Response ou WebSocket HTTP, utilisez onRequest et onWebSocket.
Documentation du Handler HTTP · Documentation du Handler WebSocket
Icônes & Noms
Personnalisez l'apparence des acteurs dans l'interface utilisateur avec les noms d'affichage et les icônes. Il est recommandé de toujours fournir un nom et une icône aux acteurs pour les rendre plus faciles à distinguer dans le tableau de bord.
import { actor } from "rivetkit";
const chatRoom = actor({
options: {
name: "Chat Room",
icon: "💬", // ou FontAwesome : "comments", "chart-line", etc.
},
// ...
});
Documentation Client
Trouvez les guides clients complets ici :
Modèles Courants
Les acteurs se mettent à l'échelle naturellement grâce à l'état isolé et au passage de messages. Structurez vos applications avec ces modèles :
Un Acteur Par Entité
Créez un acteur par utilisateur, document ou room. Utilisez des clés composées pour scoper les entités :
import { createClient } from "rivetkit/client";
import type { registry } from "./index";
const client = createClient<typeof registry>("http://localhost:6420");
// Clé unique : un acteur par utilisateur
client.user.getOrCreate(["user-123"]);
// Clé composée : document scopé à une organisation
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 } });
registry.start();
Acteurs Coordinateur & Données
Les acteurs de données gèrent la logique centrale (chat rooms, sessions de jeu, données utilisateur). Les acteurs coordinateur suivent et gèrent les collections d'acteurs de données—pensez à eux comme un index.
import { actor, setup } from "rivetkit";
// Coordinateur : suit les chat rooms d'une organisation
export const chatRoomList = actor({
state: { rooms: [] as string[] },
actions: {
addRoom: async (c, name: string) => {
// Créez l'acteur chat room
const client = c.client<typeof registry>();
await client.chatRoom.create([c.key[0], name]);
c.state.rooms.push(name);
},
listRooms: (c) => c.state.rooms,
},
});
// Acteur de données : gère une seule 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 } });
registry.start();
import { createClient } from "rivetkit/client";
import type { registry } from "./index";
const client = createClient<typeof registry>("http://localhost:6420");
// Coordinateur par org
const coordinator = client.chatRoomList.getOrCreate(["org-acme"]);
await coordinator.addRoom("general");
await coordinator.addRoom("random");
// Accédez aux chat rooms créés par coordinateur
client.chatRoom.get(["org-acme", "general"]);
Boucle Run
Utilisez une boucle run pour le travail continu de fond à l'intérieur d'un acteur. Traitez les messages de file d'attente dans l'ordre, exécutez la logique par intervalles, streamez les réponses IA ou coordonnez les tâches longue durée.
import { actor, queue, setup } from "rivetkit";
const counterWorker = actor({
state: { value: 0 },
queues: {
mutate: queue<{ delta: number }>(),
},
run: async (c) => {
for await (const message of c.queue.iter()) {
c.state.value += message.body.delta;
}
},
actions: {
getValue: (c) => c.state.value,
},
});
const registry = setup({ use: { counterWorker } });
Boucle Workflow
Utilisez ce modèle pour les workflows longue durée et durables qui initialisent les ressources, traitent les commandes dans une boucle, puis nettoient.
import { actor, queue, setup } from "rivetkit";
import { Loop, workflow } from "rivetkit/workflow";
type WorkMessage = { amount: number };
type ControlMessage = { type: "stop"; reason: string };
const worker = actor({
state: {
phase: "idle" as "idle" | "running" | "stopped",
processed: 0,
total: 0,
stopReason: null as string | null,
},
queues: {
work: queue<WorkMessage>(),
control: queue<ControlMessage>(),
},
run: workflow(async (ctx) => {
await ctx.step("setup", async () => {
await fetch("https://api.example.com/workers/init", {
method: "POST",
});
ctx.state.phase = "running";
ctx.state.stopReason = null;
});
const stopReason = await ctx.loop("worker-loop", async (loopCtx) => {
const message = await loopCtx.queue.next("wait-command", {
names: ["work", "control"],
});
if (message.name === "work") {
await loopCtx.step("apply-work", async () => {
await fetch("https://api.example.com/workers/process", {
method: "POST",
body: JSON.stringify({ amount: message.body.amount }),
});
loopCtx.state.processed += 1;
loopCtx.state.total += message.body.amount;
});
return;
}
return Loop.break((message.body as ControlMessage).reason);
});
await ctx.step("teardown", async () => {
await fetch("https://api.example.com/workers/shutdown", {
method: "POST",
});
ctx.state.phase = "stopped";
ctx.state.stopReason = stopReason;
});
}),
});
const registry = setup({ use: { worker } });
Actions vs Files d'Attente
- Actions ne sont pas durables. Utilisez-les pour les lectures en temps réel, les données éphémères et la communication à faible latence comme l'entrée joueur.
- Files d'Attente sont durables. Utilisez-les pour sérialiser les mutations via la boucle run, évitant les conditions de course avec SQLite et autres états locaux. Les appelants peuvent toujours attendre une réponse du travail en attente.
Authentification, Sécurité & CORS
- Validez les identifiants dans
onBeforeConnectoucreateConnStateet levez une erreur pour rejeter les connexions non autorisées. - Utilisez
c.conn.statepour identifier de manière sécurisée les utilisateurs dans les actions plutôt que de faire confiance aux paramètres d'action. - Pour l'accès multi-origines, validez l'origine de la demande dans
onBeforeConnect.
Documentation Authentification · Documentation CORS
Versions & Upgrades
Lors du déploiement d'un nouveau code, définissez un numéro de version afin que Rivet puisse acheminer les nouveaux acteurs vers le dernier runner et éventuellement drainer les anciens. Utilisez un timestamp de build, un nombre de commits git ou un numéro de build CI comme version. Il est très important de configurer la gestion des versions avant de déployer en production. Sans gestion des versions, les acteurs peuvent régresser en s'exécutant sur des versions de runner plus anciennes, et les acteurs existants ne seront jamais forcés de migrer vers de nouveaux runners. Ils continueront à s'exécuter indéfiniment sur les anciens runners jusqu'à leur sortie.
Anti-Modèles
Ne construisez jamais un acteur « god »
Ne mettez pas toute votre logique dans un seul acteur. Un acteur god sérialise chaque opération via un goulot d'étranglement, tue le parallélisme et fait échouer le système entier comme une unité. Divisez en acteurs fokalisés par entité.
Ne créez jamais un acteur par demande
Les acteurs sont longue durée et maintiennent l'état à travers les demandes. Créer un nouvel acteur pour chaque demande entrante jette l'avantage principal du modèle et gaspille les ressources sur la création et la suppression d'acteurs. Utilisez les acteurs pour les entités persistantes et les fonctions régulières pour le travail sans état.
Carte de Référence
Acteurs
- Contrôle d'Accès
- Actions
- Clés d'Acteur
- Planification d'Acteur
- Statuts d'Acteur
- Acteurs Rivet Générés par IA et Utilisateurs
- Authentification
- Démarrage Rapide Cloudflare Workers
- Communication Entre Acteurs
- Connexions
- Débogage
- Modèles de Conception
- Destruction d'Acteurs
- Variables Éphémères
- Erreurs
- Base de Données SQL Externe
- Handler Fetch et WebSocket
- Types d'Aide
- Icônes & Noms
- État En Mémoire
- Paramètres d'Input
- Cycle de Vie
- Limites
- Handler Requête HTTP Bas Niveau
- Stockage KV Bas Niveau
- Handler WebSocket Bas Niveau
- Métadonnées
- Démarrage Rapide Next.js
- Démarrage Rapide Node.js & Bun
- Files d'Attente & Boucles Run
- Démarrage Rapide React
- Temps Réel
- Acteur Sandbox
- Mise à l'Échelle & Concurrence
- Partage et Jointure d'État
- SQLite
- SQLite + Drizzle
- Tests
- Dépannage
- Types
- API HTTP Vanilla
- Versions & Upgrades
- Workflows
Agent OS
- Communication Agent-to-Agent
- agentOS vs Sandbox
- Authentification
- Benchmarks
- Configuration
- Package Core
- Travaux Cron
- Déploiement
- Passerelle LLM Intégrée
- Événements
- Système de Fichiers
- Limitations
- Identifiants LLM
- Multijoueur
- Mise en Réseau & Aperçus
- Aperçu
- Permissions
- Persistance & Sommeil
- Processus & Shell
- Files d'Attente
- Démarrage Rapide
- Montage Sandbox
- Sécurité & Auth
- Modèle de Sécurité
- Sessions
- Logiciel
- SQLite
- Invite de Système
- Outils
- Webhooks
- Automatisation des Workflows
Clients
Connect
- Déployer vers Amazon Web Services Lambda
- Déploiement sur AWS ECS
- Déploiement sur Cloudflare Workers
- Déploiement sur Freestyle
- Déploiement sur Google Cloud Run
- Déploiement sur Hetzner
- Déploiement sur Kubernetes
- Déploiement sur Railway
- Déploiement sur Rivet Compute
- Déploiement sur Vercel
- Déploiement sur VMs & Bare Metal
- Supabase
Cookbook
Général
- Configuration d'Acteur
- Architecture
- Partage de Ressources entre Origines
- Documentation pour LLMs & IA
- Mise en Réseau Edge
- Endpoints
- Variables d'Environnement
- Serveur HTTP
- Journalisation
- Checklist Production
- Configuration du Registre
- Modes Runtime