Add AgentOS daemon control plane and maintenance tooling
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
# Node.js & Bun Quickstart
|
||||
|
||||
> Source: `src/content/docs/actors/quickstart/backend.mdx`
|
||||
> Canonical URL: https://rivet.dev/docs/actors/quickstart/backend
|
||||
> Description: Get started with Rivet Actors in Node.js and Bun
|
||||
|
||||
---
|
||||
Prefer to start from a complete project? See the runnable [`hello-world`](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world) example.
|
||||
|
||||
## Steps
|
||||
|
||||
### 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:
|
||||
|
||||
```sh
|
||||
npx skills add rivet-dev/skills
|
||||
```
|
||||
|
||||
### Install Rivet
|
||||
|
||||
```sh
|
||||
npm install rivetkit
|
||||
```
|
||||
|
||||
### Create Actors and Start Server
|
||||
|
||||
Create a file with your actors, set up the registry, and start the server:
|
||||
|
||||
### Run Server
|
||||
|
||||
```sh Node.js
|
||||
npx tsx --watch index.ts
|
||||
```
|
||||
|
||||
```sh Bun
|
||||
bun --watch index.ts
|
||||
```
|
||||
|
||||
```sh Deno
|
||||
deno run --allow-net --allow-read --allow-env --watch index.ts
|
||||
```
|
||||
|
||||
Your server is now running on `http://localhost:6420`. Clients connect directly to the Rivet Engine on this port.
|
||||
|
||||
Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live.
|
||||
|
||||
### Connect To The Rivet Actor
|
||||
|
||||
This code can run either in your frontend or within your backend:
|
||||
|
||||
### TypeScript
|
||||
|
||||
See the [JavaScript client documentation](/docs/clients/javascript) for more information.
|
||||
|
||||
### React
|
||||
|
||||
See the [React documentation](/docs/clients/react) for more information.
|
||||
|
||||
### Deploy
|
||||
|
||||
## Configuration Options
|
||||
|
||||
_Source doc path: /docs/actors/quickstart/backend_
|
||||
@@ -0,0 +1,165 @@
|
||||
# Cloudflare Workers Quickstart
|
||||
|
||||
> Source: `src/content/docs/actors/quickstart/cloudflare.mdx`
|
||||
> Canonical URL: https://rivet.dev/docs/actors/quickstart/cloudflare
|
||||
> Description: Set up a Rivet project locally targeting Cloudflare Workers.
|
||||
|
||||
---
|
||||
Set up a Rivet project locally that runs on Cloudflare Workers. The `@rivetkit/cloudflare-workers` package wires the WebAssembly runtime for you.
|
||||
|
||||
Prefer to start from a complete project? See the runnable [`hello-world-cloudflare-workers`](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-cloudflare-workers) example (with [Hono](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-cloudflare-workers-hono) and [raw router](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-cloudflare-workers-raw) variants).
|
||||
|
||||
## Steps
|
||||
|
||||
### Install Packages
|
||||
|
||||
```sh
|
||||
npm install rivetkit @rivetkit/cloudflare-workers
|
||||
npm install --save-dev wrangler
|
||||
```
|
||||
|
||||
### Configure Wrangler
|
||||
|
||||
```toml wrangler.toml
|
||||
name = "rivetkit-cloudflare"
|
||||
main = "src/index.ts"
|
||||
compatibility_date = "2025-04-01"
|
||||
compatibility_flags = ["nodejs_compat"]
|
||||
```
|
||||
|
||||
The `nodejs_compat` flag is required so the runtime can read its connection config from `process.env`.
|
||||
|
||||
### Create the Worker
|
||||
|
||||
`createHandler` serves the Rivet manager API on `/api/rivet`. Pick how you want to handle your own routes:
|
||||
|
||||
- **Default**: Rivet handles everything.
|
||||
- **Hono**: Mount a [Hono](https://hono.dev) app for your routes (`npm install hono`).
|
||||
- **Raw**: Provide a `fetch` and route requests yourself.
|
||||
|
||||
```ts Default @nocheck
|
||||
import { actor } from "rivetkit";
|
||||
import { createHandler } from "@rivetkit/cloudflare-workers";
|
||||
|
||||
const counter = actor({
|
||||
state: { count: 0 },
|
||||
actions: {
|
||||
increment: (c, amount = 1) => {
|
||||
c.state.count += amount;
|
||||
return c.state.count;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default createHandler({ use: { counter } });
|
||||
```
|
||||
|
||||
```ts Hono @nocheck
|
||||
import { actor } from "rivetkit";
|
||||
import { createClient } from "rivetkit/client";
|
||||
import { createHandler, setup } from "@rivetkit/cloudflare-workers";
|
||||
import { Hono } from "hono";
|
||||
|
||||
const counter = actor({
|
||||
state: { count: 0 },
|
||||
actions: {
|
||||
increment: (c, amount = 1) => {
|
||||
c.state.count += amount;
|
||||
return c.state.count;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// `setup` returns a typed registry, so the client below is fully typed.
|
||||
const registry = setup({ use: { counter } });
|
||||
|
||||
const app = new Hono();
|
||||
|
||||
app.get("/", (c) => c.text("Hello from Hono + Rivet Actors!"));
|
||||
|
||||
app.post("/increment/:name", async (c) => {
|
||||
// `createClient` reads RIVET_ENDPOINT from the environment (passed by `rivet
|
||||
// dev`, or set as a Worker secret in production).
|
||||
const client = createClient<typeof registry>();
|
||||
const count = await client.counter.getOrCreate(c.req.param("name")).increment(1);
|
||||
return c.json({ count });
|
||||
});
|
||||
|
||||
// Rivet keeps `/api/rivet`; Hono handles everything else.
|
||||
export default createHandler(registry, { fetch: app.fetch });
|
||||
```
|
||||
|
||||
```ts Raw @nocheck
|
||||
import { actor } from "rivetkit";
|
||||
import { createClient } from "rivetkit/client";
|
||||
import { createHandler, setup } from "@rivetkit/cloudflare-workers";
|
||||
|
||||
const counter = actor({
|
||||
state: { count: 0 },
|
||||
actions: {
|
||||
increment: (c, amount = 1) => {
|
||||
c.state.count += amount;
|
||||
return c.state.count;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// `setup` returns a typed registry, so the client below is fully typed.
|
||||
const registry = setup({ use: { counter } });
|
||||
|
||||
// Rivet keeps `/api/rivet`; your `fetch` handles everything else.
|
||||
export default createHandler(registry, {
|
||||
fetch: async (request: Request) => {
|
||||
const url = new URL(request.url);
|
||||
|
||||
if (url.pathname === "/") {
|
||||
return new Response("Hello from a raw Rivet Worker router!");
|
||||
}
|
||||
|
||||
const increment = url.pathname.match(/^\/increment\/(.+)$/);
|
||||
if (request.method === "POST" && increment) {
|
||||
// `createClient` reads RIVET_ENDPOINT from the environment (passed by
|
||||
// `rivet dev`, or set as a Worker secret in production).
|
||||
const client = createClient<typeof registry>();
|
||||
const count = await client.counter.getOrCreate(increment[1]).increment(1);
|
||||
return Response.json({ count });
|
||||
}
|
||||
|
||||
return new Response("Not found", { status: 404 });
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Run Locally
|
||||
|
||||
Start Rivet. The CLI runs the local engine and spawns `wrangler dev` for you:
|
||||
|
||||
```sh
|
||||
npx @rivetkit/cli dev --provider cloudflare
|
||||
```
|
||||
|
||||
Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live.
|
||||
|
||||
### Connect To The Rivet Actor
|
||||
|
||||
This code can run either in your frontend or within your backend. It connects directly to the local engine on `http://localhost:6420`:
|
||||
|
||||
### TypeScript
|
||||
|
||||
See the [JavaScript client documentation](/docs/clients/javascript) for more information.
|
||||
|
||||
### React
|
||||
|
||||
See the [React documentation](/docs/clients/react) for more information.
|
||||
|
||||
### Deploy
|
||||
|
||||
Ready to ship? See [Deploying to Cloudflare Workers](/docs/deploy/cloudflare).
|
||||
|
||||
## Related
|
||||
|
||||
- [Quickstart](/docs/actors/quickstart)
|
||||
- [Deploying to Cloudflare Workers](/docs/deploy/cloudflare)
|
||||
- [SQLite](/docs/actors/sqlite)
|
||||
|
||||
_Source doc path: /docs/actors/quickstart/cloudflare_
|
||||
239
.agents/skills/rivetkit/reference/actors/quickstart/effect.md
Normal file
239
.agents/skills/rivetkit/reference/actors/quickstart/effect.md
Normal file
@@ -0,0 +1,239 @@
|
||||
# Effect.ts Quickstart (Beta)
|
||||
|
||||
> Source: `src/content/docs/actors/quickstart/effect.mdx`
|
||||
> Canonical URL: https://rivet.dev/docs/actors/quickstart/effect
|
||||
> Description: Build a Rivet Actor with the Effect SDK
|
||||
|
||||
---
|
||||
Effect support is in beta. The `@rivetkit/effect` API may change between releases. See the [`hello-world-effect`](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-effect) and [`chat-room-effect`](https://github.com/rivet-dev/rivet/tree/main/examples/chat-room-effect) examples for complete runnable projects.
|
||||
|
||||
## Steps
|
||||
|
||||
### Install Rivet
|
||||
|
||||
Add `rivetkit`, the Effect SDK, and its Effect peers:
|
||||
|
||||
```sh
|
||||
npm install rivetkit @rivetkit/effect effect @effect/platform-node
|
||||
```
|
||||
|
||||
### Define Your Actor
|
||||
|
||||
Split each actor into a public contract and a server-only implementation so the contract can be imported from client code without leaking server details.
|
||||
|
||||
The contract declares the actor and its actions. Actions are standalone values with explicit [`effect/Schema`](https://effect.website/docs/schema/introduction/) payloads and successes, validated end to end:
|
||||
|
||||
```ts src/actors/counter/api.ts @nocheck
|
||||
import { Action, Actor } from "@rivetkit/effect";
|
||||
import { Schema } from "effect";
|
||||
|
||||
export const Increment = Action.make("Increment", {
|
||||
payload: { amount: Schema.Number },
|
||||
success: Schema.Number,
|
||||
});
|
||||
|
||||
export const GetCount = Action.make("GetCount", {
|
||||
success: Schema.Number,
|
||||
});
|
||||
|
||||
export const Counter = Actor.make("Counter", {
|
||||
actions: [Increment, GetCount],
|
||||
});
|
||||
```
|
||||
|
||||
The implementation registers the actor with `.toLayer`. The wake function runs once when the actor awakes and returns the action handlers. Persisted state is accessed through a `SubscriptionRef`-like `State` API:
|
||||
|
||||
```ts src/actors/counter/live.ts @nocheck
|
||||
import { Actor, State } from "@rivetkit/effect";
|
||||
import { Effect, Schema } from "effect";
|
||||
import { Counter } from "./api.ts";
|
||||
|
||||
export const CounterLive = Counter.toLayer(
|
||||
Effect.fnUntraced(function* ({ rawRivetkitContext, state }) {
|
||||
return Counter.of({
|
||||
Increment: Effect.fnUntraced(function* ({ payload }) {
|
||||
const next = yield* State.updateAndGet(state, (current) => ({
|
||||
count: current.count + payload.amount,
|
||||
})).pipe(Effect.orDie);
|
||||
|
||||
// Broadcast the new value to every connected client.
|
||||
rawRivetkitContext.broadcast("newCount", next.count);
|
||||
|
||||
return next.count;
|
||||
}),
|
||||
GetCount: () =>
|
||||
State.get(state).pipe(
|
||||
Effect.map((current) => current.count),
|
||||
Effect.orDie,
|
||||
),
|
||||
});
|
||||
}),
|
||||
{
|
||||
state: {
|
||||
schema: Schema.Struct({ count: Schema.Number }),
|
||||
initialValue: () => ({ count: 0 }),
|
||||
},
|
||||
name: "Counter",
|
||||
icon: "calculator",
|
||||
},
|
||||
);
|
||||
```
|
||||
|
||||
### Serve The Registry
|
||||
|
||||
Compose the actor layers and serve them with `Registry.serve`. `Registry.layer()` reads engine config from the environment, and the actor layer is provided a `Client` so actors can call other actors:
|
||||
|
||||
```ts src/main.ts @nocheck
|
||||
import { NodeRuntime } from "@effect/platform-node";
|
||||
import { Client, Registry } from "@rivetkit/effect";
|
||||
import { Layer } from "effect";
|
||||
import { CounterLive } from "./actors/counter/live.ts";
|
||||
|
||||
const endpoint = process.env.RIVET_ENDPOINT ?? "http://127.0.0.1:6420";
|
||||
|
||||
const ActorsLayer = CounterLive.pipe(Layer.provide(Client.layer({ endpoint })));
|
||||
|
||||
const MainLayer = Registry.serve(ActorsLayer).pipe(Layer.provide(Registry.layer()));
|
||||
|
||||
// Keeps the layer alive. Tears down on SIGINT/SIGTERM.
|
||||
Layer.launch(MainLayer).pipe(NodeRuntime.runMain);
|
||||
```
|
||||
|
||||
### Run The Server
|
||||
|
||||
Set `RIVET_RUN_ENGINE=1` to spawn a local Rivet Engine alongside the server. The engine binary is downloaded and cached the first time you run, so there is nothing else to install:
|
||||
|
||||
```sh
|
||||
RIVET_RUN_ENGINE=1 npx tsx --watch src/main.ts
|
||||
```
|
||||
|
||||
Your server now connects to the Rivet Engine on `http://localhost:6420`. Clients connect directly to the engine on this port.
|
||||
|
||||
Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live.
|
||||
|
||||
To point at a remote engine instead, set `RIVET_ENDPOINT=https://...` and omit `RIVET_RUN_ENGINE`.
|
||||
|
||||
### Connect To The Rivet Actor
|
||||
|
||||
This code can run either in your frontend or within your backend:
|
||||
|
||||
### Effect
|
||||
|
||||
The Effect client imports the same actor contract from your registry. `Counter.client` yields a typed accessor backed by the client layer:
|
||||
|
||||
```ts src/client.ts @nocheck
|
||||
import { NodeRuntime } from "@effect/platform-node";
|
||||
import { Client } from "@rivetkit/effect";
|
||||
import { Effect } from "effect";
|
||||
import { Counter } from "./actors/counter/api.ts";
|
||||
|
||||
const program = Effect.gen(function* () {
|
||||
const counter = (yield* Counter.client).getOrCreate("my-counter");
|
||||
|
||||
const count = yield* counter.Increment({ amount: 3 });
|
||||
yield* Effect.log(`New count: ${count}`);
|
||||
|
||||
const total = yield* counter.GetCount();
|
||||
yield* Effect.log(`Total: ${total}`);
|
||||
});
|
||||
|
||||
const ClientLayer = Client.layer({ endpoint: "http://localhost:6420" });
|
||||
|
||||
program.pipe(Effect.provide(ClientLayer), NodeRuntime.runMain);
|
||||
```
|
||||
|
||||
With the server still running, start the client in another terminal:
|
||||
|
||||
```sh
|
||||
npx tsx src/client.ts
|
||||
```
|
||||
|
||||
See the [`chat-room-effect`](https://github.com/rivet-dev/rivet/tree/main/examples/chat-room-effect) example for a larger project with typed errors and actor-to-actor calls.
|
||||
|
||||
### TypeScript
|
||||
|
||||
A plain RivetKit client can call your Effect actor by name through the same engine. Actor and action names are resolved at runtime, so the client is untyped here:
|
||||
|
||||
```ts client.ts @nocheck
|
||||
import { createClient } from "rivetkit/client";
|
||||
|
||||
const client = createClient("http://localhost:6420");
|
||||
|
||||
const counter = client.Counter.getOrCreate(["my-counter"]);
|
||||
|
||||
const count = await counter.Increment({ amount: 3 });
|
||||
console.log("New count:", count);
|
||||
```
|
||||
|
||||
See the [JavaScript client documentation](/docs/clients/javascript) for more information.
|
||||
|
||||
### Deploy
|
||||
|
||||
## Feature Support
|
||||
|
||||
The Effect SDK wraps the most common actor features with typed, schema-validated APIs. Everything else is still fully usable through the raw RivetKit context (see [Raw Escape Hatch](#raw-escape-hatch) below), so no feature is off limits, it just isn't typed yet.
|
||||
|
||||
| Feature | Effect-native API | Access |
|
||||
| --- | --- | --- |
|
||||
| Actor contract & actions | `Actor.make`, `Action.make` | Typed |
|
||||
| Persisted state | `State.get` / `set` / `update` / `updateAndGet` / `changes` | Typed |
|
||||
| Typed client | `Actor.client`, `Client.layer` | Typed |
|
||||
| Typed errors | `RivetError` | Typed |
|
||||
| Logging | `RivetLogger` | Typed |
|
||||
| Sleep request | `Actor.Sleep` | Typed |
|
||||
| Actor address (`actorId` / `name` / `key`) | `Actor.CurrentAddress` | Typed |
|
||||
| Registry serve / test / web handler | `Registry` | Typed |
|
||||
| [Events & broadcast](/docs/actors/events) | Not yet wrapped | `rawRivetkitContext.broadcast(...)` |
|
||||
| [Schedule](/docs/actors/schedule) | Not yet wrapped | `rawRivetkitContext.schedule.*` |
|
||||
| [Embedded SQLite](/docs/actors/sqlite) | Not yet wrapped | `rawRivetkitContext.db.execute(...)` |
|
||||
| [Destroy](/docs/actors/lifecycle) | Not yet wrapped | `rawRivetkitContext.destroy()` |
|
||||
| Queues, connections, vars, alarms | Not yet wrapped | `rawRivetkitContext.*` |
|
||||
| Lifecycle hooks (`onSleep` / `onDestroy`) | Not yet wrapped | `rawRivetkitContext.*` |
|
||||
| Raw HTTP / WebSocket handlers | Not yet wrapped | `rawRivetkitContext.*` |
|
||||
|
||||
### Raw Escape Hatch
|
||||
|
||||
Every wake function receives `rawRivetkitContext`, the underlying RivetKit [actor context](/docs/actors). Reach for it to use any feature that does not have a typed wrapper yet. The typed `state` argument and the raw context point at the same actor, so you can mix both:
|
||||
|
||||
```ts src/actors/counter/live.ts @nocheck
|
||||
export const CounterLive = Counter.toLayer(
|
||||
Effect.fnUntraced(function* ({ rawRivetkitContext, state }) {
|
||||
return Counter.of({
|
||||
Increment: Effect.fnUntraced(function* ({ payload }) {
|
||||
// Typed state wrapper
|
||||
const next = yield* State.updateAndGet(state, (current) => ({
|
||||
count: current.count + payload.amount,
|
||||
})).pipe(Effect.orDie);
|
||||
|
||||
// Untyped features run through the raw context
|
||||
rawRivetkitContext.broadcast("newCount", next.count);
|
||||
rawRivetkitContext.schedule.after(1_000, "tick", {});
|
||||
|
||||
return next.count;
|
||||
}),
|
||||
});
|
||||
}),
|
||||
{
|
||||
state: {
|
||||
schema: Schema.Struct({ count: Schema.Number }),
|
||||
initialValue: () => ({ count: 0 }),
|
||||
},
|
||||
name: "Counter",
|
||||
},
|
||||
);
|
||||
```
|
||||
|
||||
Calls through `rawRivetkitContext` are not validated by `effect/Schema` and their payloads are typed as they are in the base RivetKit API.
|
||||
|
||||
## Next Steps
|
||||
|
||||
|
||||
- [Actions](/docs/actors/actions) — Define the RPC surface clients call on your actor.
|
||||
|
||||
|
||||
- [State](/docs/actors/state) — Persist and load actor state across sleeps and restarts.
|
||||
|
||||
|
||||
- [Events](/docs/actors/events) — Broadcast realtime updates to connected clients.
|
||||
|
||||
_Source doc path: /docs/actors/quickstart/effect_
|
||||
117
.agents/skills/rivetkit/reference/actors/quickstart/next-js.md
Normal file
117
.agents/skills/rivetkit/reference/actors/quickstart/next-js.md
Normal file
@@ -0,0 +1,117 @@
|
||||
# Next.js Quickstart
|
||||
|
||||
> Source: `src/content/docs/actors/quickstart/next-js.mdx`
|
||||
> Canonical URL: https://rivet.dev/docs/actors/quickstart/next-js
|
||||
> Description: Get started with Rivet Actors in Next.js
|
||||
|
||||
---
|
||||
### 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:
|
||||
|
||||
```sh
|
||||
npx skills add rivet-dev/skills
|
||||
```
|
||||
|
||||
### Create a Next.js App
|
||||
|
||||
```sh
|
||||
npx create-next-app@latest my-app
|
||||
cd my-app
|
||||
```
|
||||
|
||||
### Install RivetKit
|
||||
|
||||
### Create an Actor
|
||||
|
||||
Create a file at `src/rivet/registry.ts` with a simple counter actor:
|
||||
|
||||
### Setup Rivet API route
|
||||
|
||||
Create a file at `src/app/api/rivet/[...all]/route.ts` to setup the API routes:
|
||||
|
||||
```ts src/app/api/rivet/[...all]/route.ts @nocheck
|
||||
import { toNextHandler } from "@rivetkit/next-js";
|
||||
import { registry } from "@/rivet/registry";
|
||||
|
||||
export const maxDuration = 300;
|
||||
|
||||
export const { GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS } = toNextHandler(registry);
|
||||
```
|
||||
|
||||
### Use the Actor in a component
|
||||
|
||||
Create a Counter component and add it to your page:
|
||||
|
||||
```tsx src/components/Counter.tsx @nocheck
|
||||
"use client";
|
||||
|
||||
import { createRivetKit } from "@rivetkit/next-js/client";
|
||||
import type { registry } from "@/rivet/registry";
|
||||
import { useState } from "react";
|
||||
|
||||
export const { useActor } = createRivetKit<typeof registry>({
|
||||
endpoint:
|
||||
process.env.NEXT_PUBLIC_RIVET_ENDPOINT ?? "http://localhost:3000/api/rivet",
|
||||
namespace: process.env.NEXT_PUBLIC_RIVET_NAMESPACE,
|
||||
token: process.env.NEXT_PUBLIC_RIVET_TOKEN,
|
||||
});
|
||||
|
||||
export function Counter() {
|
||||
const [count, setCount] = useState(0);
|
||||
|
||||
// Get or create a counter actor for the key "my-counter"
|
||||
const counter = useActor({
|
||||
name: "counter",
|
||||
key: ["my-counter"]
|
||||
});
|
||||
|
||||
// Listen to realtime events
|
||||
counter.useEvent("newCount", (x: number) => setCount(x));
|
||||
|
||||
const increment = async () => {
|
||||
// Call actions
|
||||
await counter.connection?.increment(1);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>Count: {count}</p>
|
||||
<button onClick={increment}>Increment</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
```tsx src/app/page.tsx @nocheck
|
||||
import { Counter } from "@/components/Counter";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<main>
|
||||
<h1>My App</h1>
|
||||
<Counter />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
For information about the Next.js client API, see the [React Client API Reference](/docs/clients/react).
|
||||
|
||||
### Run Locally
|
||||
|
||||
Start the Next.js dev server. Rivet auto-starts a local engine alongside it:
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Open `http://localhost:3000` in your browser to use the app.
|
||||
|
||||
Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live.
|
||||
|
||||
### Deploy to Vercel
|
||||
|
||||
See the [Vercel deployment guide](/docs/deploy/vercel) for detailed instructions on deploying your Rivet app to Vercel.
|
||||
|
||||
_Source doc path: /docs/actors/quickstart/next-js_
|
||||
82
.agents/skills/rivetkit/reference/actors/quickstart/react.md
Normal file
82
.agents/skills/rivetkit/reference/actors/quickstart/react.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# React Quickstart
|
||||
|
||||
> Source: `src/content/docs/actors/quickstart/react.mdx`
|
||||
> Canonical URL: https://rivet.dev/docs/actors/quickstart/react
|
||||
> Description: Build realtime React applications with Rivet Actors
|
||||
|
||||
---
|
||||
## Steps
|
||||
|
||||
### 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:
|
||||
|
||||
```sh
|
||||
npx skills add rivet-dev/skills
|
||||
```
|
||||
|
||||
### Install Dependencies
|
||||
|
||||
```sh
|
||||
npm install rivetkit @rivetkit/react
|
||||
```
|
||||
|
||||
### Create Backend Actor and Start Server
|
||||
|
||||
Create your actor registry on the backend and start the server:
|
||||
|
||||
### Create React Frontend
|
||||
|
||||
Set up your React application:
|
||||
|
||||
For detailed information about the React client API, see the [React Client API Reference](/docs/clients/react).
|
||||
|
||||
### Setup Vite Configuration
|
||||
|
||||
Configure Vite for development:
|
||||
|
||||
```ts vite.config.ts @nocheck
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
server: {
|
||||
port: 5173,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### Run Your Application
|
||||
|
||||
Start both the backend and frontend:
|
||||
|
||||
**Terminal 1**: Start the backend
|
||||
|
||||
```sh Node.js
|
||||
npx tsx --watch backend/index.ts
|
||||
```
|
||||
|
||||
```sh Bun
|
||||
bun --watch backend/index.ts
|
||||
```
|
||||
|
||||
```sh Deno
|
||||
deno run --allow-net --allow-read --allow-env --watch backend/index.ts
|
||||
```
|
||||
|
||||
**Terminal 2**: Start the frontend
|
||||
|
||||
```sh Frontend
|
||||
npx vite
|
||||
```
|
||||
|
||||
Open `http://localhost:5173` in your browser. Try opening multiple tabs to see realtime sync in action.
|
||||
|
||||
Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live.
|
||||
|
||||
### Deploy
|
||||
|
||||
## Configuration Options
|
||||
|
||||
_Source doc path: /docs/actors/quickstart/react_
|
||||
249
.agents/skills/rivetkit/reference/actors/quickstart/rust.md
Normal file
249
.agents/skills/rivetkit/reference/actors/quickstart/rust.md
Normal file
@@ -0,0 +1,249 @@
|
||||
# Rust Quickstart (Beta)
|
||||
|
||||
> Source: `src/content/docs/actors/quickstart/rust.mdx`
|
||||
> Canonical URL: https://rivet.dev/docs/actors/quickstart/rust
|
||||
> Description: Build a Rivet Actor in Rust
|
||||
|
||||
---
|
||||
Rust support is in beta. The supported public Rust API is `rivetkit` and `rivetkit-client`; lower-level crates are internal implementation details and do not carry a stability guarantee. See the full API reference on [docs.rs/rivetkit](https://docs.rs/rivetkit), or the runnable [`hello-world-rust`](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-rust) example.
|
||||
|
||||
## Steps
|
||||
|
||||
### Add Rivet
|
||||
|
||||
Add the `rivetkit` crate and its companions:
|
||||
|
||||
```sh
|
||||
cargo add rivetkit anyhow async-trait
|
||||
cargo add serde --features derive
|
||||
cargo add tokio --features full
|
||||
```
|
||||
|
||||
### Define Your Actor
|
||||
|
||||
Put the actor in `src/lib.rs` so both your server and your client can share the same types. An actor is a type that implements `Actor`, plus one `Handles` implementation for each action. Persisted state lives in `type State`; ephemeral runtime state is just fields on your actor struct.
|
||||
|
||||
```rust src/lib.rs
|
||||
use std::{future::Future, pin::Pin, sync::Arc};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use rivetkit::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
type BoxFuture<T> = Pin<Box<dyn Future<Output = Result<T>> + Send>>;
|
||||
|
||||
pub struct Counter;
|
||||
|
||||
#[derive(Default, Serialize, Deserialize)]
|
||||
pub struct CounterState {
|
||||
pub count: i64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Increment {
|
||||
pub amount: i64,
|
||||
}
|
||||
|
||||
impl Action for Increment {
|
||||
type Output = i64;
|
||||
|
||||
const NAME: &'static str = "increment";
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct NewCount {
|
||||
pub count: i64,
|
||||
}
|
||||
|
||||
impl Event for NewCount {
|
||||
const NAME: &'static str = "newCount";
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Actor for Counter {
|
||||
type State = CounterState;
|
||||
type Input = ();
|
||||
type Actions = (Increment,);
|
||||
type Events = (NewCount,);
|
||||
type Queue = ();
|
||||
type ConnParams = ();
|
||||
type ConnState = ();
|
||||
type Action = action::Raw;
|
||||
|
||||
async fn create_state(_ctx: &Ctx<Self>, _input: Self::Input) -> Result<Self::State> {
|
||||
Ok(CounterState::default())
|
||||
}
|
||||
|
||||
async fn create(_ctx: &Ctx<Self>) -> Result<Self> {
|
||||
Ok(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Handles<Increment> for Counter {
|
||||
type Future = BoxFuture<i64>;
|
||||
|
||||
fn handle(self: Arc<Self>, ctx: Ctx<Self>, action: Increment) -> Self::Future {
|
||||
Box::pin(async move {
|
||||
let count = {
|
||||
let mut state = ctx.state_mut();
|
||||
state.count += action.amount;
|
||||
state.count
|
||||
};
|
||||
ctx.emit(NewCount { count })?;
|
||||
Ok(count)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn registry() -> Registry {
|
||||
let mut registry = Registry::new();
|
||||
registry.register_actor::<Counter>("counter");
|
||||
registry
|
||||
}
|
||||
```
|
||||
|
||||
### Serve The Registry
|
||||
|
||||
Your `src/main.rs` just starts the registry from the library:
|
||||
|
||||
```rust src/main.rs
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
counter::registry().start().await
|
||||
}
|
||||
```
|
||||
|
||||
Replace `counter` with your crate name (the package `name` in `Cargo.toml`, with dashes as underscores).
|
||||
|
||||
### Run The Server
|
||||
|
||||
The Rust runtime connects to the Rivet Engine. Setting `RIVETKIT_ENGINE_AUTO_DOWNLOAD=1` lets the runtime download and cache a matching engine binary the first time you run, so there is nothing else to install:
|
||||
|
||||
```sh
|
||||
RIVETKIT_ENGINE_AUTO_DOWNLOAD=1 cargo run
|
||||
```
|
||||
|
||||
Your server now connects to the Rivet Engine on `http://localhost:6420`. Clients connect directly to the engine on this port.
|
||||
|
||||
Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live.
|
||||
|
||||
Already have an engine binary? Set `RIVET_ENGINE_BINARY_PATH=/path/to/rivet-engine` to point at it instead. If you are working inside the [Rivet monorepo](https://github.com/rivet-dev/rivet), a local `cargo build -p rivet-engine` is discovered automatically from `target/debug`.
|
||||
|
||||
### Connect To The Rivet Actor
|
||||
|
||||
This code can run either in your frontend or within your backend:
|
||||
|
||||
### Rust
|
||||
|
||||
Add a `src/bin/client.rs` that imports the same actor types from your library. There is no need to redefine the actor on the client.
|
||||
|
||||
```rust src/bin/client.rs
|
||||
use counter::{Counter, Increment, NewCount};
|
||||
use rivetkit::{
|
||||
client::{Client, ClientConfig},
|
||||
prelude::*,
|
||||
TypedClientExt,
|
||||
};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default"));
|
||||
|
||||
let counter = client.get_or_create_typed_default::<Counter>("counter", ["my-counter"])?;
|
||||
let count = counter.send(Increment { amount: 3 }).await?;
|
||||
println!("New count: {count}");
|
||||
|
||||
let connection = counter.connect();
|
||||
connection
|
||||
.on::<NewCount>(|event| println!("Count changed: {}", event.count))
|
||||
.await;
|
||||
connection.send(Increment { amount: 1 }).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
With the server still running, start the client in another terminal:
|
||||
|
||||
```sh
|
||||
cargo run --bin client
|
||||
```
|
||||
|
||||
See the [`hello-world-rust`](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-rust) example for a complete runnable counter.
|
||||
|
||||
### TypeScript
|
||||
|
||||
A TypeScript client can call your Rust actor by name through the same engine. Actor and action names are resolved at runtime, so the client is untyped here:
|
||||
|
||||
```ts client.ts @nocheck
|
||||
import { createClient } from "rivetkit/client";
|
||||
|
||||
const client = createClient("http://localhost:6420");
|
||||
|
||||
const counter = client.counter.getOrCreate(["my-counter"]);
|
||||
|
||||
const counterConnection = counter.connect();
|
||||
counterConnection.on("newCount", (event) => {
|
||||
console.log("Event count:", event.count);
|
||||
});
|
||||
|
||||
const count = await counterConnection.increment(3);
|
||||
console.log("New count:", count);
|
||||
|
||||
await counterConnection.increment(1);
|
||||
```
|
||||
|
||||
See the [JavaScript client documentation](/docs/clients/javascript) for more information.
|
||||
|
||||
### React
|
||||
|
||||
```tsx Counter.tsx @nocheck
|
||||
import { createRivetKit } from "@rivetkit/react";
|
||||
import { useState } from "react";
|
||||
|
||||
const { useActor } = createRivetKit("http://localhost:6420");
|
||||
|
||||
function Counter() {
|
||||
const [count, setCount] = useState(0);
|
||||
|
||||
const counter = useActor({
|
||||
name: "counter",
|
||||
key: ["my-counter"],
|
||||
});
|
||||
|
||||
const increment = async () => {
|
||||
await counter.connection?.increment(1);
|
||||
};
|
||||
|
||||
counter.useEvent("newCount", (event) => {
|
||||
setCount(event.count);
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>Count: {count}</p>
|
||||
<button onClick={increment}>Increment</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
See the [React documentation](/docs/clients/react) for more information.
|
||||
|
||||
### Deploy
|
||||
|
||||
## Next Steps
|
||||
|
||||
|
||||
- [API Reference](https://docs.rs/rivetkit) — Full `rivetkit` crate documentation on docs.rs.
|
||||
|
||||
|
||||
- [Actions](/docs/actors/actions) — Define the RPC surface clients call on your actor.
|
||||
|
||||
|
||||
- [State](/docs/actors/state) — Persist and load actor state across sleeps and restarts.
|
||||
|
||||
|
||||
- [Events](/docs/actors/events) — Broadcast realtime updates to connected clients.
|
||||
|
||||
_Source doc path: /docs/actors/quickstart/rust_
|
||||
109
.agents/skills/rivetkit/reference/actors/quickstart/supabase.md
Normal file
109
.agents/skills/rivetkit/reference/actors/quickstart/supabase.md
Normal file
@@ -0,0 +1,109 @@
|
||||
# Supabase Functions Quickstart
|
||||
|
||||
> Source: `src/content/docs/actors/quickstart/supabase.mdx`
|
||||
> Canonical URL: https://rivet.dev/docs/actors/quickstart/supabase
|
||||
> Description: Set up a Rivet project locally targeting Supabase Edge Functions.
|
||||
|
||||
---
|
||||
Set up a Rivet project locally that runs on Supabase Edge Functions. The `@rivetkit/supabase` package wires the WebAssembly runtime for you.
|
||||
|
||||
Prefer to start from a complete project? See the runnable [`hello-world-supabase-functions`](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-supabase-functions) example.
|
||||
|
||||
## Steps
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- [Node.js](https://nodejs.org/)
|
||||
- [Supabase CLI](https://supabase.com/docs/guides/cli)
|
||||
- Docker, for Supabase's local Edge Runtime
|
||||
|
||||
The CLI runs the local Rivet engine as a bundled native binary, so Docker is only needed for Supabase itself. A Supabase project is only needed to deploy.
|
||||
|
||||
### Create the Function
|
||||
|
||||
```sh
|
||||
npx supabase functions new rivet
|
||||
```
|
||||
|
||||
Add the packages used by the function:
|
||||
|
||||
```sh
|
||||
npm install rivetkit @rivetkit/supabase
|
||||
```
|
||||
|
||||
### Configure the Function
|
||||
|
||||
Call `serve` from `@rivetkit/supabase`. It loads the WebAssembly runtime and serves the Rivet handler.
|
||||
|
||||
```ts supabase/functions/rivet/index.ts @nocheck
|
||||
import { actor } from "rivetkit";
|
||||
import { serve, setup } from "@rivetkit/supabase";
|
||||
|
||||
const counter = actor({
|
||||
state: { count: 0 },
|
||||
actions: {
|
||||
increment: (c, amount = 1) => {
|
||||
c.state.count += amount;
|
||||
return c.state.count;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// `setup` returns a typed registry, so a client can type itself with
|
||||
// `typeof registry`.
|
||||
export const registry = setup({ use: { counter } });
|
||||
|
||||
await serve(registry);
|
||||
```
|
||||
|
||||
Add a `deno.json` next to the function so the deploy bundles only the WebAssembly runtime. It points `rivetkit` at the pre-bundled `@rivetkit/supabase`, keeping the deploy small. Without it, the deploy pulls Rivet's native engine and 413s.
|
||||
|
||||
```json supabase/functions/rivet/deno.json
|
||||
{
|
||||
"imports": {
|
||||
"rivetkit": "npm:@rivetkit/supabase",
|
||||
"@rivetkit/supabase": "npm:@rivetkit/supabase"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Your function code keeps importing from `rivetkit` as usual. The import map only changes how Deno resolves it at bundle time.
|
||||
|
||||
### Run Locally
|
||||
|
||||
Start Rivet. The CLI runs the local engine, spawns `supabase functions serve` for you, and populates the connection values:
|
||||
|
||||
```sh
|
||||
npx @rivetkit/cli dev --provider supabase
|
||||
```
|
||||
|
||||
Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live.
|
||||
|
||||
### Call the Actor
|
||||
|
||||
Connect to your actor from a client. This connects directly to the local engine on `http://localhost:6420`:
|
||||
|
||||
```ts client.ts @nocheck
|
||||
import { createClient } from "rivetkit/client";
|
||||
import type { registry } from "./supabase/functions/rivet/index";
|
||||
|
||||
const client = createClient<typeof registry>("http://localhost:6420");
|
||||
|
||||
const counter = client.counter.getOrCreate(["my-counter"]);
|
||||
const count = await counter.increment(3);
|
||||
console.log("New count:", count);
|
||||
```
|
||||
|
||||
See the [JavaScript client documentation](/docs/clients/javascript) for more information.
|
||||
|
||||
### Deploy
|
||||
|
||||
Ready to ship? See [Deploying to Supabase Functions](/docs/deploy/supabase).
|
||||
|
||||
## Related
|
||||
|
||||
- [Quickstart](/docs/actors/quickstart)
|
||||
- [Deploying to Supabase Functions](/docs/deploy/supabase)
|
||||
- [SQLite](/docs/actors/sqlite)
|
||||
|
||||
_Source doc path: /docs/actors/quickstart/supabase_
|
||||
Reference in New Issue
Block a user