在 Supabase Edge Functions(Deno)中运行 RivetKit Actors:@rivetkit/supabase 集成指南

发布时间:2026/9/18 11:48:31
在 Supabase Edge Functions(Deno)中运行 RivetKit Actors:@rivetkit/supabase 集成指南 在 Supabase Edge FunctionsDeno中运行 RivetKit Actorsrivetkit/supabase 集成指南【免费下载链接】actorsRivet Actors are the primitive for stateful workloads. Built for AI agents, collaborative apps, and durable execution.项目地址: https://gitcode.com/GitHub_Trending/riv/actors导读rivetkit/supabase是 RivetKit 官方提供的 Supabase Edge FunctionsDeno 运行时适配层只需一行导入即可把 RivetKit Actors 以 WebAssemblywasm运行时形态托管在 Supabase Edge Function 中wasm 运行时与 wasm 二进制加载全部自动接线。阅读本文后你将掌握如何编写一个最小的 Actor 计数器函数、如何通过环境变量/配置注入 Rivet Engine 连接信息、如何挂载自定义路由如/health健康检查、理解管理器 API 路径的挂载与 Supabase 函数前缀剥离机制以及打包体积优化的底层原理。关联文档rivetkit-typescript/packages/supabase/README.md本文所有实现细节均可在 rivetkit-typescript/packages/supabase/src/mod.ts 等源码中找到证据。一、为什么需要 rivetkit/supabaseRivetKit Actors 是面向有状态负载stateful workloads的运行时原语适用于 AI Agent、协作应用与持久化执行等场景。在服务端函数serverless环境中托管 Actors需要解决两个关键问题运行时适配Edge 环境没有本地进程/原生二进制必须使用 wasm 运行时。依赖闭包精简Supabase Edge Function 的部署Deno eszip会快照整个 npm 依赖闭包。若直接声明rivetkit为运行时依赖会把其 native 包engine-cli、Services、rivetkit-napi、agent-os secure-exec 等全部拖入部署产物而这些代码在 wasm 路径下永远不会被执行。rivetkit/supabase的解法是在 tsup.config.ts 中把rivetkit预打包进自身 dist不将其声明为运行时依赖同时通过函数的 import map 将rivetkit指向该预打包适配器。这样部署产物只包含 wasm 路径真正用到的代码。该包在 package.json 中以Apache-2.0协议发布导出 ESM/CJS 双格式dist/mod.mjs与dist/mod.js。二、最小可运行示例计数器 Actor这是 README 给出的完整最小示例行为等价于仓库示例 examples/hello-world-supabase-functions/supabase/functions/rivet/index.tsimport { actor } from rivetkit; import { serve } from rivetkit/supabase; const counter actor({ state: { count: 0 }, actions: { increment: (c, amount 1) (c.state.count amount), getCount: (c) c.state.count, }, }); await serve({ use: { counter } });要点拆解actor(...)来自rivetkit的创作 API声明有状态 Actor 的state与actionsserve来自rivetkit/supabase它完成两件事加载 wasm 运行时、托管 Rivet 管理器 API用户的源码仍然写import { actor } from rivetkit由 import map 重定向到该适配器详见下文函数目录结构。连接配置需要将RIVET_ENDPOINT配置为函数密钥function secret。命名空间与令牌可以内嵌在 URL 中https://namespace:tokenhost例如https://my-ns:my-tokenapi.rivet.dev。从源码看applyEnv的读取优先级是代码中显式配置优先环境变量兜底配置项环境变量说明endpointRIVET_ENDPOINTRivet Engine 端点必填URL 支持namespace:tokenhost鉴权语法namespaceRIVET_NAMESPACE命名空间可选可从 URL 内嵌tokenRIVET_TOKEN访问令牌可选可从 URL 内嵌envoy.poolNameRIVET_POOL运行池名称仅当配置未显式设置时写入serverless.basePath—管理器 API 基础路径默认为/api/rivetRIVET_* 系列环境变量在 rivetkit-typescript/packages/rivetkit/src/utils/env-vars.ts 中统一定义RIVET_ENDPOINT也由核心配置 schema 支持见 rivetkit-typescript/packages/rivetkit/src/registry/config/index.ts。函数目录结构仓库示例examples/hello-world-supabase-functions 给出了完整的本地可运行布局hello-world-supabase-functions/ ├── package.json # dev: npx rivetkit/cli dev --provider supabase ├── scripts/client.ts # 类型化客户端调用计数器 Actor └── supabase/ ├── config.toml # project_id、edge_runtime 开关 └── functions/rivet/ ├── index.ts # Actor 定义 setup/serve └── deno.json # import maprivetkit - npm:rivetkit/supabase其中 deno.json 是关键将rivetkit与rivetkit/supabase都映射到npm:rivetkit/supabaseDeno 才能解析导入同时保持部署产物精简{ imports: { rivetkit: npm:rivetkit/supabase, rivetkit/supabase: npm:rivetkit/supabase } }本地运行与调用README 中的运行流程仓库示例 examples/hello-world-supabase-functions/README.mdnpm install npx supabase start # 先启动本地 Supabase 栈Edge Functions serve 依赖它 npm run dev # rivet dev 启动本地 Engine 并拉起 supabase functions serve另开终端调用 Actornpm run clientscripts/client.ts 展示了类型化客户端的用法——从函数模块导出registry客户端侧用createClienttypeof registry(...)继承全部 Actor 类型然后直接client.counter.getOrCreate(demo)调用increment/getCount方法import { createClient } from rivetkit/client; import type { registry } from ../supabase/functions/rivet/index.ts; const client createClienttypeof registry({ endpoint: process.env.RIVET_ENDPOINT ?? http://localhost:6420, }); const counter client.counter.getOrCreate(demo); console.log(increment(3) - ${await counter.increment(3)}); console.log(getCount() - ${await counter.getCount()});网络注意点本地 Edge Runtime 运行在容器中它访问宿主机上的 Engine 需要使用http://host.docker.internal:6420而非回环地址。Linux 下supabase functions serve会提供所需的host-gateway映射macOS/Windows 由 Docker Desktop 提供该别名因此跨平台可用仅当需要覆盖时才手动设置RIVET_ENDPOINT。生产部署命令为npx supabase functions deploy rivet三、setup 与 serve两个入口的职责划分rivetkit/supabase暴露了两个核心导出实现在 rivetkit-typescript/packages/supabase/src/mod.ts3.1setup(config)setup是对rivetkit核心setup的包装它自动注入 wasm 运行时并返回类型化 Registryexport function setupA extends RegistryActors( config: SupabaseSetupConfigA, ): RegistryA { return rivetkitSetupA({ runtime: wasm, wasm: { bindings: wasmBindings }, noWelcome: true, ...config, }); }关键点强制runtime: wasm绑定rivetkit/rivetkit-wasm的 wasm bindingsSupabaseSetupConfig从RegistryConfigInput中剥离了runtime与wasm两个字段mod.ts因为这两个字段由适配器接管用户无需也不应手动配置由于 wasm 二进制由serve异步读取setup保持同步返回值是RegistryA可以用createClienttypeof registry(...)派生类型化客户端并可将同一 registry 传给serve。3.2serve(registryOrConfig, options?)serve是异步的返回Promisevoid接受两种入参setup返回的Registry实例直接传配置对象内部自动走一遍setup。它内部完成const wasmModule await Deno.readFile(resolveWasmUrl()); config.wasm { ...config.wasm, bindings: wasmBindings, initInput: wasmModule };通过import.meta.resolve(rivetkit/rivetkit-wasm/rivetkit_wasm_bg.wasm)定位 wasm 二进制再用Deno.readFile异步读出将二进制作为initInput注入 wasm 运行时最终调用Deno.serve(...)启动 HTTP 服务。ServeOptions支持两个字段字段默认值说明managerPath/api/rivetRivet 管理器 API 的挂载路径fetch无管理器路径之外请求的自定义处理器managerPath的解析优先级为config.serverless?.basePath→options.managerPath→ 默认值/api/rivet。3.3 wasm 运行时绑定rivetkit/rivetkit-wasm是 wasm 核心绑定包rivetkit-typescript/packages/rivetkit-wasm/index.js 导出pkg/rivetkit_wasm.js及rivetkit_wasm_bg.wasmRust 核心源码位于 rivetkit-typescript/packages/rivetkit-wasm/src/lib.rs通过 wasm-pack 面向wasm32-unknown-unknown构建。这解释了为什么在无本地进程的 Edge 环境中 Actors 依然能够运行。四、路由机制管理器 API 与自定义 fetch4.1 默认行为Rivet 管理器 APIserve在Deno.serve处理器中执行如下路由逻辑mod.ts命中管理器路径url.pathname managerPath或以其为前缀managerPath /直接交给registry.handler(request)Supabase 函数前缀剥离Supabase 把函数挂载在/functions/v1/name下因此管理器 API 实际可能出现在如/name/api/rivet/...。适配器会查找路径中第一次出现的管理器段并剥离前缀后再交给registry.handler从而用户无需配置 Supabase 特定的 basePath自定义 fetch以上都不命中且提供了options.fetch则交给自定义处理器兜底响应否则返回This is a RivetKit server.\n\nLearn more at https://rivet.dev\n。registry.handler内部rivetkit-typescript/packages/rivetkit/src/registry/index.ts会识别 serverless 的POST {basePath}/start与GET {basePath}/metadata协议端点校验 start 请求体大小默认上限 16 MiB超限返回 413并通过流式响应处理背压。serverless.basePath的默认值与配置 schema 定义在 rivetkit-typescript/packages/rivetkit/src/registry/config/serverless.ts。4.2 挂载自定义路由README 展示了如何通过fetch处理管理器 API 之外的所有请求await serve({ use: { counter } }, { fetch: (request) { if (new URL(request.url).pathname.endsWith(/health)) { return new Response(ok); } return new Response(not found, { status: 404 }); }, });这是健康检查等自定义端点的标准写法/health返回ok其余非管理器路径返回 404。仓库测试 rivetkit-typescript/packages/rivetkit/tests/platforms/supabase-functions.test.ts 中函数实现也采用了完全相同的模式/health返回ok其余 404可作为端到端验证参考。五、打包优化原理源码级tsup.config.ts 的实现揭示了部署精简的关键策略预打包 rivetkitrivetkit被 bundle 进适配器的 dist且不声明为运行时依赖从而 Deno eszip 不会把 rivetkit 的 native 依赖闭包rivetkit/rivetkit-napi、rivetkit/engine-cli、rivet-dev/services、rivet-dev/agent-os-core拖进部署产物外部化白名单仅rivetkit/rivetkit-wasm与少量 Node CommonJS 库pino、cbor-x保持外部依赖在运行时由 Deno 的 Node 兼容层加载node: 前缀重写通过 esbuild 插件把所有裸 Node 内置模块导入重写为node:前缀形式如node:os因为 esbuild 打包 CJS 依赖时可能产出裸import module而 Deno 会拒绝这种写法。此外package.json 的check-edge-closure脚本node ../../../scripts/ci/check-edge-native-closure.mjs会在 CI 中校验边缘部署产物不包含 native 依赖闭包防止回归。六、配置参考RegistryConfigInput 常用字段serve/setup的配置对象继承rivetkit的RegistryConfigInput去除了runtime与wasm。以下常用字段均可显式传入或在applyEnv中由环境变量兜底schema 见 rivetkit-typescript/packages/rivetkit/src/registry/config/index.ts 与 serverless.ts配置字段类型/默认值说明use记录必需Actor 定义表键为 Actor 名endpointstringEngine 端点支持https://namespace:tokenhostURL 鉴权语法也可用RIVET_ENDPOINTnamespacestring默认default命名空间也可用RIVET_NAMESPACEtokenstring访问令牌也可用RIVET_TOKENenvoy.poolNamestring默认default运行池名也可用RIVET_POOLserverless.basePath/api/rivet管理器 API 基础路径与managerPath等价serverless.maxStartPayloadBytes16 MiBPOST /start请求体大小上限serverless.publicEndpoint自动推断客户端应连接的公共端点支持 URL 鉴权语法也可用RIVET_PUBLIC_ENDPOINTserverless.publicToken自动推断公共端点令牌也可用RIVET_PUBLIC_TOKENsqlitelocal/remote/{ backend }SQLite 后端wasm 运行时默认为remote且不可用localnoWelcomefalse禁用启动欢迎日志适配器默认已设为truelogging.levelwarn日志级别七、端到端验证仓库测试如何覆盖该集成仓库提供了针对 Supabase Functions 平台的冒烟测试 rivetkit-typescript/packages/rivetkit/tests/platforms/supabase-functions.test.ts它验证了与本文描述完全一致的链路先以rivet-engine start --config ...启动本地 Engine写入临时config.json配置 guard/api-peer/metrics 端口与拓扑生成临时 Supabase 项目supabase/config.toml启用edge_runtime、supabase/functions/rivet/actor.tsSQLite 计数器 Actor、index.tssetup({ use: {...}, sqlite: remote })serve(registry, { fetch })、functions/.env写入RIVET_ENDPOINT、RIVET_PUBLIC_ENDPOINT、RIVET_NAMESPACE、RIVET_POOL、RIVET_TOKEN将rivetkit、rivetkit/rivetkit-wasm、rivetkit/supabase等依赖拷贝进函数目录的node_modules保证函数与测试共享同一份工作区代码通过supabase start排除大部分不需要的服务与supabase functions serve --no-verify-jwt --env-file supabase/functions/.env拉起本地 Supabase 栈测试断言冷启动后increment(2)返回 2连续increment(3)返回 5getCount()为 5休眠 1.5s 后再次访问wakeCount增加验证休眠唤醒三个并行 Actor 各自计数正确验证并发隔离。这个测试从实战角度印证了连接配置全部来自函数环境变量、/health自定义路由可用、cold start 后 Actor 状态持久化、休眠唤醒与并行 Actor 均正常工作与 README 中以单一 import 托管 Actors的描述完全吻合。八、使用建议与限制推荐写法函数内先const registry setup({ use: { ... } })并导出便于客户端createClienttypeof registry获得全量类型再await serve(registry, options)。健康检查通过options.fetch在/health返回okSupabase 平台与本地测试均按此约定探测。wasm 运行时的 SQLite 限制wasm 运行时默认使用remoteSQLite 后端且不能使用local见 rivetkit-typescript/packages/rivetkit/src/registry/config/index.ts有状态持久化依赖远端 Engine因此RIVET_ENDPOINT是必需配置。本地调试npx supabase start必须先于supabase functions serve执行容器网络下 Engine 地址使用host.docker.internal主机名。部署体积务必保留deno.jsonimport maprivetkit→npm:rivetkit/supabase否则部署会拖入 native 依赖闭包。相关资源适配器源码rivetkit-typescript/packages/supabase/src/mod.ts打包配置rivetkit-typescript/packages/supabase/tsup.config.tswasm 绑定rivetkit-typescript/packages/rivetkit-wasm核心配置 schemarivetkit-typescript/packages/rivetkit/src/registry/config/index.ts可运行示例examples/hello-world-supabase-functions平台冒烟测试rivetkit-typescript/packages/rivetkit/tests/platforms/supabase-functions.test.ts【免费下载链接】actorsRivet Actors are the primitive for stateful workloads. Built for AI agents, collaborative apps, and durable execution.项目地址: https://gitcode.com/GitHub_Trending/riv/actors创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考