Convex Components:概念、隔离模型与使用方式
8月 12, 2026
AI 参与说明(Agent:/root、/root/components_docs、/root/components_source、/root/components_landscape):本文由 Agent 根据 Convex 官方文档、Components Directory、官方 npm 包说明与公开源码辅助整理,重点核对 Component 的隔离边界、调用语义、安装步骤和示例 API。资料核验于 2026-08-12;Components、CLI 与各个包的 API 都可能演进,实施前请以文末一手资料、项目 lockfile 和目标版本为准。
适用范围:本文面向已有 Convex TypeScript 项目、想复用现成后端能力或准备编写自己的 Component 的开发者。文中示例需要一个已配置 authentication 的 development deployment;示例只说明 application-layer rate limiting,不替代 network-layer DDoS 防护、身份认证或第三方服务的安全审查。
先说结论#
Convex Components 不是 UI 组件,也不只是“从 npm 安装一个 helper”。它是一个可安装到现有 Convex deployment 的、带私有状态的 backend module:可以包含 functions、schema、database tables、File Storage、scheduled functions 和独立的函数执行环境。应用与 Component 通过显式 API 交互,而不是互相直接读写表。Components Overview Understanding Components
这带来三项实际价值:
- 使用方可以把 rate limiting、durable workflow、aggregate、AI agent 等有状态能力作为一个明确的后端边界接入,而非把私有实现表混进应用 schema。
- Component 作者可以封装内部表、索引、调度和数据不变量,只公开稳定的 function API。
- 调用仍发生在同一 Convex backend 的 transaction 模型内;它不是为了复用功能而额外引入一个需要自己运维的 microservice。
不过,官方 API reference 目前仍将 Components 相关的 AppDefinition、ComponentDefinition 和 FunctionHandle 标记为 beta/unstable。因此,生产项目应锁定 convex 与 Component npm 包版本,在 dev 或 preview deployment 运行 codegen、typecheck 和关键 integration test 后再升级。convex/server API reference
1. Components 解决的是什么问题#
可以把一个 Component 理解为“可组合的小型 Convex backend”,而不是普通的 TypeScript library。
| 选择 | 状态与执行位置 | 适用情形 | 不适合的情形 |
|---|---|---|---|
| 普通 npm library | 在应用自身的函数环境中运行,直接使用应用传入的 ctx | 纯算法、格式化、无私有持久状态的共享代码 | 需要隐藏自己的 tables、scheduler 或复杂状态机 |
| 应用内模块 | 使用主应用 schema 与主应用 functions | 业务数据只服务当前应用,且与主领域模型紧密耦合 | 希望跨多个项目复用并独立维护数据不变量 |
| Convex Component | 自带 functions、schema、data 与 sandbox;通过 ComponentApi 调用 | rate limiting、workflow、aggregate、协作、集成等可复用且有持久状态的能力 | 只需一两个纯函数,或必须由外部团队独立运行、独立扩缩容的服务 |
| 外部服务 | 独立网络/运维边界,通常经 action 调用 | 第三方支付、邮件、LLM、公司已有的独立系统 | 只是为了在 Convex 内复用一个有状态模块 |
@convex-dev/rate-limiter 是一个直观例子:它可以把分片、窗口算法和私有表藏在 Component 内,只让应用声明限流规则并调用 limit()。@convex-dev/workflow 则把持久化步骤和重放日志封装起来。两者都不是 Convex 内核本身,而是建立在 Convex transaction、scheduler 和 functions 之上的应用层能力。
2. 隔离模型:有边界,但不是另一个 SaaS#
Component instance 形成一棵树:主应用在根,主应用或其他 Component 可以安装 child Component。同一个 Component 以不同 name 多次 use() 时,会得到各自独立的 tables 和 functions。Authoring Components Using Components
flowchart TD
browser["Browser / React client"] --> publicFn["App public query / mutation / action<br/>authentication、authorization 与业务校验"]
publicFn --> componentApi["components.rateLimiter<br/>ComponentApi"]
componentApi --> sandbox["Rate Limiter Component sandbox<br/>functions · schema · private tables · scheduler"]
sandbox --> result["validated result / error"]
result --> publicFn
publicFn --> browser
callback["Explicit FunctionHandle<br/>仅在需要回调 app 时传入"] --> sandbox这个模型的关键不变量如下:
- Component 不能任意读取主应用的 tables、File Storage、environment variables 或 scheduled functions;主应用也不能直接修改 Component 数据。
- Component 的 global variables 和对运行环境的 patch 不会与主应用或其他 Component 共享。
- 跨边界的数据只通过 function arguments、return values 或显式
FunctionHandle流动;arguments 与 return values 仍受 runtime validation 约束。 - 这种隔离只说明 Convex 的 data/function boundary,不等于一个第三方 npm 包已经通过安全、许可或供应链审计。安装前仍应阅读该包的 README、LICENSE、release、依赖和可能的 external API / webhook 行为。
官方将 Components Directory 定义为目录,而不是单一的官方支持级别。目录中既有社区项目,也有 Convex 团队项目;若只看团队维护的条目,应使用目录提供的 get-convex / @convex-dev 官方清单。每个 Component 的许可证、维护状态和外部依赖都应单独核对。Components Directory
3. 使用一个 Component 的标准流程#
以下四步适用于大多数 npm Component。这里选用官方 Rate Limiter,因为它同时展示了安装、codegen、宿主应用 wrapper 与 transaction 语义。
3.1 安装并注册#
前提是已有一个 Convex 项目,并且 convex/ 已是该项目的 backend 目录。若已经有 convex/convex.config.ts,应把 app.use(...) 合并进去,不要覆盖已有 Component 配置。
npm install @convex-dev/rate-limiter// convex/convex.config.ts
import { defineApp } from "convex/server";
import rateLimiter from "@convex-dev/rate-limiter/convex.config.js";
const app = defineApp();
// 默认 instance name 是 "rateLimiter"。
app.use(rateLimiter);
export default app;随后运行开发命令:
npx convex dev --oncenpx convex dev 会针对已配置的 dev deployment 更新 generated code 并推送当前 backend 代码;成功后,应用的 convex/_generated/api 才会拥有 components.rateLimiter。如果 Component 从未注册或 codegen 尚未成功,就不应手写或强制转换这个类型。npx convex dev Using Components
3.2 在宿主应用创建一个 TypeScript facade#
RateLimiter class 是这个 Component 提供的高层 facade。它内部会用 components.rateLimiter 调用 Component API;应用侧只保留业务可读的限流名称和配置。
// convex/rateLimiter.ts
import { MINUTE, RateLimiter } from "@convex-dev/rate-limiter";
import { components } from "./_generated/api.js";
export const rateLimiter = new RateLimiter(components.rateLimiter, {
sendMessage: {
kind: "token bucket",
rate: 10,
period: MINUTE,
capacity: 3,
},
});上例的 token bucket 以每分钟 10 个 token 的速率恢复容量,capacity: 3 限制空闲后一次可积累的突发额度。它只是示例参数;应按用户、团队、API 配额和可接受的峰值来设计。对于高并发的热点规则,Component 还提供 shards,但分片是降低 transaction contention 的工程手段,不是无限扩容承诺。Rate Limiter README
3.3 由应用的 public mutation 承担 authentication 和 authorization#
Component 的 functions 不能被 browser/React client 直接当作主应用 api 调用。主应用看到的 components.rateLimiter 是 ComponentApi:其中 Component 的 public functions 在跨边界后成为 internal references,只能在 Convex function 中通过 ctx.runQuery、ctx.runMutation 或 ctx.runAction 调用。应在主应用 wrapper 中完成 authentication、authorization、输入校验和业务写入。Component API
下面是可放进一个已启用 authentication 的 TypeScript 项目的最小骨架。tokenIdentifier 从服务端 identity 得到,而不是让 client 自报限流 key。
// convex/schema.ts
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";
export default defineSchema({
messages: defineTable({
author: v.string(),
body: v.string(),
}),
});// convex/messages.ts
import { v } from "convex/values";
import { mutation } from "./_generated/server.js";
import { rateLimiter } from "./rateLimiter.js";
export const send = mutation({
args: { body: v.string() },
returns: v.id("messages"),
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity();
if (identity === null) {
throw new Error("Unauthenticated");
}
await rateLimiter.limit(ctx, "sendMessage", {
key: identity.tokenIdentifier,
throws: true,
});
return await ctx.db.insert("messages", {
author: identity.tokenIdentifier,
body: args.body,
});
},
});预期行为是:已认证用户调用 api.messages.send 时,服务端先检查并消费 sendMessage 的额度;额度不足时 throws: true 抛出 ConvexError,未被捕获的错误会使整个 top-level mutation 失败,消息不会插入。不要用 client 传入的 userId、IP 或任意字符串作为授权依据;若需要 anonymous 限流,应在服务端设计可信的 session / device / challenge 机制。
本文没有连接或创建真实 Convex deployment 来执行该示例;代码结构已按当前官方文档和 Rate Limiter 源码核对。接入自己的项目时,运行 npx convex dev --once,预期会完成 codegen、typecheck 并把 Component 安装到已配置的 dev deployment;随后用你的已认证 client integration test 验证“超限时不写入消息”这一业务不变量。
这个例子同时说明了正确分层:ctx.auth 在主应用 function 可用,而 Rate Limiter Component 只收到被显式传入的 key。Component 无法顺藤摸瓜读取主应用用户表或身份配置。
4. 跨 Component 调用仍保留 Convex function 规则#
components.foo 不是浏览器可公开调用的 api.foo。调用者的 function 类型决定能调用 Component 的哪类 function:
| 调用者 | 可调用的 Component function | 结果 |
|---|---|---|
query | query | 仍是 reactive query;不能写入 Component 数据。 |
mutation | query、mutation | 可以把主应用和 Component 的写入放进同一 top-level transaction。 |
action | query、mutation、action | 可触达 external I/O 型 Component action,但仍要遵守 action 的幂等与重试边界。 |
对 mutation 而言,transaction 行为比“调用了另一个模块”更细致:
- top-level mutation 成功时,主应用与所有 Component mutation 的写入一起 commit;top-level mutation 未捕获异常时,相关写入一起 rollback。
- 单次 Component mutation 又是独立的 sub-transaction:如果它抛出异常且主应用捕获了异常,只有那次 Component 调用会 rollback,主应用可选择继续其他写入。
- 因此,是否捕获 Component 错误是业务语义选择。上面的
throws: true不捕获,适合“限流失败就拒绝发消息”;开发环境旁路等场景才可能有意识地捕获它。
这不是分布式两阶段提交:它是在 Convex 的同一 transaction 模型内提供的跨 Component 原子性与可捕获的 sub-transaction 行为。Transactions across Components
5. 常见边界与易错点#
| 主题 | 正确理解 | 实施建议 |
|---|---|---|
Id 类型 | Component boundary 外,Id<"table"> 会变为普通 string;目前不能在一个 Component 中用 v.id() 验证主应用或另一个 Component 的 table。 | 传递跨边界 ID 时把它当 opaque string,在拥有该表的边界内再验证业务归属。 |
| Authentication | Component 内没有 ctx.auth。 | 在主应用 wrapper 做 authentication/authorization,再显式传入 userId、tenant ID 或经过授权的能力。 |
| Environment variables | Component 不能任意读取主应用的 process.env;普遍可用的系统变量仅包括 CONVEX_CLOUD_URL 和 CONVEX_SITE_URL。 | Component 作者在 defineComponent(..., { env }) 声明 typed environment variables;安装方在 app.use(..., { env }) 显式绑定;只在 handler 内读取。 |
| HTTP Actions | Component 的 http.ts route 默认不暴露。 | 安装时使用 { httpPrefix: "/my-component/" } 才会把 /hello 暴露为 /my-component/hello;不要把 Rust 内部的 mount_path 误写成用户 API。 |
| Component HTTP authentication | Component HTTP action 同样没有 ctx.auth,也不能读取 app environment variables。 | 需要用户认证或应用数据时,在主应用 convex/http.ts 写受控 wrapper。 |
| Pagination | 内置 .paginate() 不能直接用于 Components 的 reactive pagination。 | Component 作者应使用 convex-helpers 的 paginator;需要 React usePaginatedQuery 时也用对应 helper。 |
| 回调主应用 | Component 不能随意调用 parent app function。 | 使用显式的 FunctionHandle;它是可序列化的 reference,但目标 function 仍可能在后续 code push 中被删除。 |
httpPrefix 是当前公开 TypeScript API 的名字。主应用自己的 defineApp({ httpPrefix }) 不会自动影响 child Component 的 HTTP routes;Component 的 prefix 始终相对于 deployment URL 根路径。未设置 Component httpPrefix 时,Component route 不会占用主应用的 URL 空间。HTTP Routes HTTP Actions
6. 如何从 Directory 选择 Component#
目录提供的是发现入口,不是“所有条目同等经过官方支持”的承诺。选择前可以按如下顺序排查:
- 先确认需求是否真的需要独立持久状态:例如
Rate Limiter、Workflow、Aggregate、Agent和Workpool都是 Component 的典型场景;纯格式化或一次性业务逻辑则不必引入 Component。 - 区分社区条目和 Convex 团队条目;团队条目可从
get-convex/@convex-dev清单核对,但仍要读该项目的 README、release 与 LICENSE。 - 阅读 Component 暴露的 function API,特别检查它要求的
env、Function Handle、HTTP route、external webhook/API key 和 migration 方案。 - 为 package 和
convex版本保留 lockfile;升级先在 dev/preview deployment 执行 codegen、typecheck 与针对业务 wrapper 的 integration test。 - 在 Dashboard 用 Component selector 观察它独立的数据、functions、files 与 logs;使用
convex-test时必须注册 Component,许多 package 会提供/testhelper。Testing Components
Components 本身是开源代码和数据,不收取一项单独的“Component 使用费”;但它们会按实现方式消耗对应的 Convex usage,例如 function execution、database、File Storage 或 external service 调用。成本评估应以具体 Component 的数据模型、调用频率和所用套餐为准。Components Directory FAQ
7. 编写自己的 Component:最小结构与责任边界#
当需求确实需要可复用的私有状态时,可以先做 local Component,再决定是否打包发布。官方建议把 local Component 放在 convex/components/,其目录结构与普通 convex/ backend 类似,但拥有自己的 generated code:
convex/components/myComponent/
├── _generated/ # Component 专属 generated code
├── convex.config.ts # defineComponent 与 child Components
├── schema.ts # 仅 Component 可访问的 schema
└── lib.ts # queries / mutations / actions// convex/components/myComponent/convex.config.ts
import { defineComponent } from "convex/server";
const component = defineComponent("myComponent");
export default component;// convex/convex.config.ts
import { defineApp } from "convex/server";
import myComponent from "./components/myComponent/convex.config.js";
const app = defineApp();
app.use(myComponent);
export default app;本地 Component 修改后运行:
npx convex dev --once --typecheck-componentsComponent 内的 function 定义应从自身的 ./_generated/server.js 导入 builders,而不是误用主应用的 convex/_generated/server.js。它的 public functions 构成 parent 可见的 API;应为它们提供 args 与 returns validators。要发布 npm package 时,可从官方模板开始:
npx create-convex@latest --component打包的 Component 比本地 Component 多一层构建顺序:先对 Component 本身执行 codegen,再构建 npm package,最后由 example app 执行 npx convex dev --typecheck-components。官方 authoring 文档也说明 package 至少需要导出 convex.config.js 和 _generated/component.js 等使用入口。Authoring Components
8. 从源码理解 Components:三个推荐入口#
若希望从使用方式继续深入实现,可按“app facade → Component public API → 私有 schema/算法”的方向读。以下是 2026-08-12 核对的公开源码快照,便于将本文概念落回真实实现:
| Component | 建议入口 | 值得研究的机制 |
|---|---|---|
rate-limiter | src/client/index.ts → src/component/lib.ts → schema.ts | facade 如何调用 ComponentApi、私有 rateLimits 表、token bucket / fixed window 与分片。 |
workflow | src/client/step.ts → journal.ts → schema.ts | deterministic replay、持久化 steps/events 与 Workpool 调度。 |
aggregate | src/client/index.ts → public.ts → btree.ts | private B-tree、count/sum/rank,以及为什么主应用必须在同一 mutation 中显式同步 insert/replace/delete。 |
Aggregate 是理解隔离代价的好反例:它不能自动观察主应用表,因此应用的每一条写路径都要显式维护 aggregate;Convex mutation 的原子性保证主表写入和 aggregate 更新一起 commit 或 rollback。不要把“Component 有独立数据”误解为“平台会替你同步任意两张表”。
更底层的 Component 配置、类型检查与 HTTP route 装配,可沿上一篇 Convex 源码导读:开源仓库版图、核心架构与阅读路线 中的 convex-js 与 convex-backend 阅读路线继续追踪。
结语#
正确使用 Convex Components 的关键不是“少写代码”,而是先画清数据和权限边界:主应用负责 client-facing API、authentication、authorization 与领域数据;Component 负责它自己封装的状态与算法;两者只用经过验证的 function API、arguments 和必要时的 FunctionHandle 协作。
一旦按这条边界组织,Component 可以同时保留普通 library 的易复用性、外部服务的封装性,以及 Convex transaction 的一致性。反之,若把 components.* 直接当作 browser API、把 Component 当作可以读取所有 app 数据的插件,或忽略版本/许可证/external side effect 审查,就会失去它最有价值的安全和维护优势。
一手资料#
- Convex Components Overview · Understanding Components · Using Components · Authoring Components
- Components Directory · Convex team Components list
convex/serverComponent API reference ·npx convex dev·npx convex deploy- Rate Limiter Component source · Workflow Component source · Aggregate Component source