website/src/content/docs/actors/quickstart/cloudflare-workers.mdx
import { Hosting } from "@/components/docs/Hosting";
<Steps> <Step title="Add Rivet Skill to Coding Agent (Optional)">If you're using an AI coding assistant (like Claude Code, Cursor, Windsurf, etc.), add Rivet skills for enhanced development assistance:
npx skills add rivet-dev/skills
npm install rivetkit @rivetkit/cloudflare-workers
Create a simple counter actor:
import { actor, setup } from "rivetkit";
export const counter = actor({
state: { count: 0 },
actions: {
increment: (c, x: number) => {
c.state.count += x;
c.broadcast("newCount", c.state.count);
return c.state.count;
},
},
});
export const registry = setup({
use: { counter },
});
Choose your preferred web framework:
<Tabs> <Tab title="Default"> <CodeGroup workspace> ```ts actors.ts @hide import { actor, setup } from "rivetkit";export const counter = actor({ state: { count: 0 }, actions: { increment: (c, x: number) => { c.state.count += x; c.broadcast("newCount", c.state.count); return c.state.count; }, }, });
export const registry = setup({ use: { counter }, });
```ts index.ts
import { createHandler } from "@rivetkit/cloudflare-workers";
import { registry } from "./actors";
// The `/api/rivet` endpoint is automatically exposed here for external clients
const { handler, ActorHandler } = createHandler(registry);
export { handler as default, ActorHandler };
export const counter = actor({ state: { count: 0 }, actions: { increment: (c, x: number) => { c.state.count += x; c.broadcast("newCount", c.state.count); return c.state.count; }, }, });
export const registry = setup({ use: { counter }, });
```ts index.ts
import { createHandler, type Client } from "@rivetkit/cloudflare-workers";
import { Hono } from "hono";
import { registry } from "./actors";
const app = new Hono<{ Bindings: { RIVET: Client<typeof registry> } }>();
app.post("/increment/:name", async (c) => {
const client = c.env.RIVET;
const name = c.req.param("name");
// Get or create actor and call action
const counter = client.counter.getOrCreate([name]);
const newCount = await counter.increment(1);
return c.json({ count: newCount });
});
// The `/api/rivet` endpoint is automatically exposed here for external clients
const { handler, ActorHandler } = createHandler(registry, { fetch: app.fetch });
export { handler as default, ActorHandler };
export const counter = actor({ state: { count: 0 }, actions: { increment: (c, x: number) => { c.state.count += x; c.broadcast("newCount", c.state.count); return c.state.count; }, }, });
export const registry = setup({ use: { counter }, });
```ts index.ts @nocheck
import { createHandler } from "@rivetkit/cloudflare-workers";
import { registry } from "./actors";
// The `/api/rivet` endpoint is automatically mounted on this router for external clients
const { handler, ActorHandler } = createHandler(registry, {
fetch: async (request, env, ctx) => {
const url = new URL(request.url);
if (url.pathname.startsWith("/increment/")) {
const name = url.pathname.split("/")[2];
const client = env.RIVET;
const counter = client.counter.getOrCreate([name]);
const newCount = await counter.increment(1);
return new Response(JSON.stringify({ count: newCount }), {
headers: { "Content-Type": "application/json" },
});
}
return new Response("Not Found", { status: 404 });
}
});
export { handler as default, ActorHandler };
export const counter = actor({ state: { count: 0 }, actions: { increment: (c, x: number) => { c.state.count += x; c.broadcast("newCount", c.state.count); return c.state.count; }, }, });
export const registry = setup({ use: { counter }, });
```ts index.ts @nocheck
import { createInlineClient } from "@rivetkit/cloudflare-workers";
import { registry } from "./actors";
const {
client,
fetch: rivetFetch,
ActorHandler,
} = createInlineClient(registry);
// IMPORTANT: Your Durable Object must be exported here
export { ActorHandler };
export default {
fetch: async (request, env, ctx) => {
const url = new URL(request.url);
// Custom request handler
if (request.method === "POST" && url.pathname.startsWith("/increment/")) {
const name = url.pathname.slice("/increment/".length);
const counter = client.counter.getOrCreate([name]);
const newCount = await counter.increment(1);
return new Response(JSON.stringify({ count: newCount }), {
headers: { "Content-Type": "application/json" },
});
}
// Optional: Mount /api/rivet path to access actors from external clients
if (url.pathname.startsWith("/api/rivet")) {
const strippedPath = url.pathname.substring("/api/rivet".length);
url.pathname = strippedPath;
const modifiedRequest = new Request(url.toString(), request);
return rivetFetch(modifiedRequest, env, ctx);
}
return new Response("Not Found", { status: 404 });
},
} satisfies ExportedHandler;
Configure your wrangler.json for Cloudflare Workers:
{
"name": "my-rivetkit-app",
"main": "src/index.ts",
"compatibility_date": "2025-01-20",
"compatibility_flags": ["nodejs_compat"],
"migrations": [
{
"tag": "v1",
"new_sqlite_classes": ["ActorHandler"]
}
],
"durable_objects": {
"bindings": [
{
"name": "ACTOR_DO",
"class_name": "ActorHandler"
}
]
},
"kv_namespaces": [
{
"binding": "ACTOR_KV",
"id": "your_namespace_id"
}
]
}
Start the development server:
wrangler dev
Your server is now running at http://localhost:8787
Test your counter actor using HTTP requests:
<CodeGroup>// Increment counter
const response = await fetch("http://localhost:8787/increment/my-counter", {
method: "POST"
});
const result = await response.json();
console.log("Count:", result.count); // 1
# Increment counter
curl -X POST http://localhost:8787/increment/my-counter
Deploy to Cloudflare's global edge network:
wrangler deploy
Your actors will now run on Cloudflare's edge with persistent state backed by Durable Objects.
See the Cloudflare Workers deployment guide for detailed deployment instructions and configuration options.
</Step> </Steps>Create a type-safe client to connect from your frontend or another service:
<Tabs> <Tab title="JavaScript"> <CodeGroup workspace> ```ts actors.ts @hide import { actor, setup } from "rivetkit";export const counter = actor({ state: { count: 0 }, actions: { increment: (c, x: number) => { c.state.count += x; c.broadcast("newCount", c.state.count); return c.state.count; }, }, });
export const registry = setup({ use: { counter }, });
```ts client.ts
import { createClient } from "rivetkit/client";
import type { registry } from "./actors";
// Create typed client (use your deployed URL)
const client = createClient<typeof registry>("https://your-app.workers.dev/api/rivet");
// Use the counter actor directly
const counter = client.counter.getOrCreate(["my-counter"]);
// Call actions
const count = await counter.increment(3);
console.log("New count:", count);
// Listen to real-time events
const connection = counter.connect();
connection.on("newCount", (newCount: number) => {
console.log("Count changed:", newCount);
});
// Increment through connection
await connection.increment(1);
See the JavaScript client documentation for more information.
</Tab> <Tab title="React"> <CodeGroup workspace> ```ts actors.ts @hide import { actor, setup } from "rivetkit";export const counter = actor({ state: { count: 0 }, actions: { increment: (c, x: number) => { c.state.count += x; c.broadcast("newCount", c.state.count); return c.state.count; }, }, });
export const registry = setup({ use: { counter }, });
```tsx Counter.tsx
import { createRivetKit } from "@rivetkit/react";
import { useState } from "react";
import type { registry } from "./actors";
const { useActor } = createRivetKit<typeof registry>("https://your-app.workers.dev/api/rivet");
function Counter() {
const [count, setCount] = useState(0);
const counter = useActor({
name: "counter",
key: ["my-counter"]
});
counter.useEvent("newCount", (x: number) => setCount(x));
const increment = async () => {
await counter.connection?.increment(1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}
See the React documentation for more information.
</Tab> </Tabs> <Note> Cloudflare Workers mounts the Rivet endpoint on `/api/rivet` by default. </Note>