Platforms
Deploying to Cloudflare Workers
Run RivetKit on Cloudflare Workers with the WebAssembly runtime.
Cloudflare Workers run RivetKit through the WebAssembly runtime. Use the public @rivetkit/rivetkit-wasm package, pass the bindings through setup({ wasm }), and use remote SQLite.
Steps
Prerequisites
- Cloudflare account
wranglerconfigured for your account- A Rivet namespace from the Rivet Dashboard or a self-hosted Rivet Engine
Install Packages
npm install rivetkit @rivetkit/rivetkit-wasm
npm install --save-dev wrangler
Configure Wrangler
Set your Rivet connection values as Worker variables. The pool name must match the serverless runner configured in Rivet.
name = "rivetkit-cloudflare"
main = "src/index.ts"
compatibility_date = "2025-04-01"
compatibility_flags = ["nodejs_compat"]
[vars]
RIVET_ENDPOINT = "https://api.rivet.dev"
RIVET_NAMESPACE = "your-namespace"
RIVET_POOL = "cloudflare-workers"
RIVET_TOKEN = "sk_..."
RIVET_PUBLIC_ENDPOINT = "https://your-namespace:pk_...@api.rivet.dev"
Create the Worker
This example uses raw SQL to keep the runtime setup visible. When runtime: "wasm" is used, unset SQLite defaults to remote SQLite, and sqlite: "local" is rejected.
import { actor, setup } from "rivetkit";
import * as wasmBindings from "@rivetkit/rivetkit-wasm";
import wasmModule from "@rivetkit/rivetkit-wasm/rivetkit_wasm_bg.wasm";
interface Env {
RIVET_ENDPOINT: string;
RIVET_NAMESPACE: string;
RIVET_POOL: string;
RIVET_TOKEN: string;
RIVET_PUBLIC_ENDPOINT: string;
}
interface SqliteDatabase {
run(sql: string, params?: unknown[]): Promise<void>;
query(sql: string, params?: unknown[]): Promise<{ rows: unknown[][] }>;
writeMode<T>(callback: () => Promise<T>): Promise<T>;
}
const rawSqlDatabaseProvider = {
createClient: async () => ({
execute: async () => [],
close: async () => {},
}),
onMigrate: async () => {},
};
const counter = actor({
db: rawSqlDatabaseProvider,
actions: {
increment: async (ctx, amount = 1) => {
const db = ctx.sql as SqliteDatabase;
await db.writeMode(async () => {
await db.run(
"CREATE TABLE IF NOT EXISTS counters (id INTEGER PRIMARY KEY, count INTEGER NOT NULL)",
);
await db.run(
"INSERT INTO counters (id, count) VALUES (1, ?) ON CONFLICT(id) DO UPDATE SET count = count + excluded.count",
[amount],
);
});
const result = await db.query("SELECT count FROM counters WHERE id = 1");
return Number(result.rows[0]?.[0] ?? 0);
},
},
});
const use = { counter };
let registry: { handler(request: Request): Promise<Response> } | undefined;
function getRegistry(env: Env) {
registry ??= setup({
runtime: "wasm",
sqlite: "remote",
wasm: {
bindings: wasmBindings,
initInput: wasmModule,
},
use,
endpoint: env.RIVET_ENDPOINT,
namespace: env.RIVET_NAMESPACE,
token: env.RIVET_TOKEN,
envoy: {
poolName: env.RIVET_POOL,
},
serverless: {
publicEndpoint: env.RIVET_PUBLIC_ENDPOINT,
},
});
return registry;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
return await getRegistry(env).handler(request);
},
};
Deploy
npx wrangler deploy
After deploy, set the Worker URL with the /api/rivet path as the serverless runner URL in Rivet.
Runtime Notes
- Use
runtime: "wasm"insetup(...)for Workers. You can also setRIVETKIT_RUNTIME=wasmin environments where the registry config does not setruntime. - Pass
wasm: { bindings, initInput }explicitly from@rivetkit/rivetkit-wasm. - Use remote SQLite on Workers. Leaving SQLite unset with
runtime: "wasm"selects remote SQLite automatically. - Keep
RIVET_PUBLIC_ENDPOINTpointed at the client-facing Rivet endpoint. Register the Worker URL separately as the serverless runner URL. - Local Workers runtimes must support outbound WebSockets for the Rivet envoy connection.