news 2026/9/24 9:18:24

在 Convex 中编写 Query 与 Mutation 函数:基于 tsgo-test 示例的完整实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
在 Convex 中编写 Query 与 Mutation 函数:基于 tsgo-test 示例的完整实战指南
  • 数据库
  • 后端

【免费下载链接】convex-backend

The open-source reactive database for app developers

项目地址:https://gitcode.com/gh_mirrors/co/convex-backend
点击查看免费下载

导读

本文以开源仓库 convex-backend 中npm-packages/private-demos/tsgo-test演示项目为背景,完整讲解 Convex 函数(Functions)目录的标准组织方式:如何编写带参数校验(Validator)的 query 与 mutation 函数、如何在 React 客户端调用它们、如何通过 Convex CLI 将函数推送到部署环境,以及如何用 TypeScript 7 原生编译器 tsgo 对函数进行类型检查。读完本文,你将掌握一套可直接复制到任何 Convex 项目中的最小可运行函数代码骨架,并理解其背后 CLI 类型检查的实现原理。


一、Convex 函数目录是什么

在 Convex 应用中,服务端代码存放在项目的convex/目录中(即"函数目录")。这个目录里的每个 TypeScript/JavaScript 文件都会被打包并在 Convex 的云端或自托管后端中执行,构成应用的"服务端逻辑"。

以仓库中的npm-packages/private-demos/tsgo-test/convex/目录为例,其标准结构包含:

  • README.md:官方生成的函数目录说明模板(即本文依据的核心文档);
  • example.ts:一个最小可运行的 query 函数示例;
  • _generated/:由npx convex dev自动生成的类型与 API 绑定代码(api.tsserver.tsdataModel.ts等);
  • tsconfig.json:用于对 Convex 函数做类型检查的 TypeScript 工程配置。

_generated目录是自动生成、不应手工修改的。在 server.d.ts 文件头部明确写着:"THIS CODE IS AUTOMATICALLY GENERATED. To regenerate, runnpx convex dev.",其中导出了querymutationactioninternalQueryinternalMutationhttpAction等全部服务端函数构造器,以及QueryCtxMutationCtxDatabaseReaderDatabaseWriter等上下文类型。这些类型化的导出正是下面所有函数示例的类型安全基础。

二、编写一个带参数校验的 query 函数

2.1 函数定义骨架

query 函数用于读取数据库,是 Convex 中默认"只读、可被客户端订阅"的函数。官方模板给出的标准写法如下(对应文档原文,可在 README.md 中查看):

// convex/myFunctions.ts import { query } from "./_generated/server"; import { v } from "convex/values"; export const myQueryFunction = query({ // Validators for arguments. args: { first: v.number(), second: v.string(), }, // Function implementation. handler: async (ctx, args) => { // Read the database as many times as you need here. const documents = await ctx.db.query("tablename").collect(); // Arguments passed from the client are properties of the args object. console.log(args.first, args.second); // Write arbitrary JavaScript here: filter, aggregate, build derived data, // remove non-public properties, or create new objects. return documents; }, });

拆解这个骨架,有三个关键点:

  1. args中的 Validator 是 Convex 的核心安全机制v.number()v.string()来自convex/values包,运行时会对客户端传入的每个参数做校验,类型不匹配的函数调用会被直接拒绝,从而避免"脏数据"进入数据库查询。除上述两种外,v还提供v.id()v.object()v.array()v.union()v.optional()等完整校验器集合,并支持.optional()链式写法。
  2. handler的第一个参数ctx是函数上下文ctx.db提供数据库访问能力。对 query 而言,ctx.db的类型是只读的DatabaseReader(见 server.d.ts),只能getquery,无法写入。
  3. handler可以写任意 JS 逻辑:在返回前进行过滤、聚合、派生数据、剥离非公开字段等处理都是推荐做法——这相当于把"数据脱敏与业务加工"放在服务端完成。

2.2 最小可运行示例:仓库里的真实代码

上述模板是教学示例;仓库中tsgo-test演示项目实际部署了一个极简 query 函数,见 example.ts:

import { query } from "./_generated/server"; export const hello = query({ args: {}, handler: async (): Promise<string> => { return "Hello from TypeScript!"; }, });

这个hello函数没有参数(args: {}),不做任何数据库访问,直接返回一个字符串。它虽然简单,却完整演示了 Convex 函数的最小闭环:定义 → 生成 API 绑定 → 被客户端调用。在生成产物 api.d.ts 中可以看到,api对象通过ApiFromModules自动收集了example模块下所有导出的函数引用,客户端即可通过api.example.hello类型安全地调用它。

2.3 在 React 中调用 query

Convex 为 React 提供了useQueryHook,模板中的用法如下:

const data = useQuery(api.myFunctions.myQueryFunction, { first: 10, second: "hello", });

useQuery会自动完成三件事:订阅该查询、在数据变化时触发组件重新渲染、在组件卸载时取消订阅。它接收的参数对象与args中声明的 Validator 一一对应,类型由_generated/api.d.ts从函数定义中推导,因此参数写错会在编译期直接报错。

三、编写一个带参数校验的 mutation 函数

3.1 函数定义骨架

mutation 函数用于写入数据库(也可读取),并具备原子性保证。官方模板如下:

// convex/myFunctions.ts import { mutation } from "./_generated/server"; import { v } from "convex/values"; export const myMutationFunction = mutation({ // Validators for arguments. args: { first: v.string(), second: v.string(), }, // Function implementation. handler: async (ctx, args) => { // Insert or modify documents in the database here. // Mutations can also read from the database like queries. const message = { body: args.first, author: args.second }; const id = await ctx.db.insert("messages", message); // Optionally, return a value from your mutation. return await ctx.db.get("messages", id); }, });

关键差异点:

  • ctx.db是读写类型DatabaseWriter:除getquery外还提供insertpatchreplacedelete等写操作;
  • 原子性保证:单个 mutation 内的所有写入会被原子地提交(见 server.d.ts 中DatabaseWriter的文档注释),不会出现"写了一半"的中间状态,也天然规避了乐观并发控制下的部分写问题;
  • 可以返回值handler的返回值会被序列化后传回客户端,便于客户端拿到刚插入文档的_id做后续跳转或 UI 更新。

3.2 在 React 中调用 mutation

mutation 在 React 中通过useMutationHook 调用,模板给出了两种典型用法:

const mutation = useMutation(api.myFunctions.myMutationFunction); function handleButtonPress() { // fire and forget, the most common way to use mutations mutation({ first: "Hello!", second: "me" }); // OR // use the result once the mutation has completed mutation({ first: "Hello!", second: "me" }).then((result) => console.log(result), ); }
  • Fire-and-forget(推荐):多数 UI 场景下不关心返回值,直接调用即可,Convex 客户端会负责把结果同步到所有订阅相关查询的组件;
  • 获取结果mutation(...)返回 Promise,.then()中拿到的正是服务端handler的返回值(如上面示例中插入后重新读回的完整文档)。

四、推送函数与 CLI 工具链

4.1 常用 CLI 命令

函数写好后,需要通过 Convex CLI 与部署环境交互。文档明确给出了两条基础命令:

  • 查看 CLI 全部能力:在项目根目录运行npx convex -h
  • 启动本地文档:运行npx convex docs会打开本地/在线的 Convex 文档站点。

实际开发中最常用的还有:

  • npx convex dev:本地开发模式,持续监听convex/目录,自动完成代码生成(_generated)与函数推送,并启动本地后端;
  • npx convex deploy:将函数推送到生产部署;
  • npx convex codegen --init:在缺少convex/tsconfig.json时创建类型检查所需的工程配置。

4.2 CLI 的类型检查实现

CLI 在每次推送前都会对函数目录执行 TypeScript 类型检查。仓库中的核心实现在 typecheck.ts:

  • 编译器解析优先级resolveTypescriptCompiler,第33-39行):CLI 命令行参数 →convex.json中的typescriptCompiler字段 → 默认"tsc"
  • 类型检查模式TypeCheckMode,第21行):enable(失败即中止推送)、try(找不到编译器时降级跳过)、disable(完全跳过,通过--typecheck=disable启用);
  • 检查入口:读取convex/tsconfig.json,若不存在则跳过并提示运行npx convex codegen --init(第120-128行);
  • 慢检查提示:当单次类型检查超过 10 秒阈值(SLOW_TYPECHECK_THRESHOLD_MS)时,CLI 会切换 spinner 并给出性能排查建议(第25-27行、第69-73行)。

4.3 使用 tsgo(TypeScript 7 原生编译器)

tsgo-test这个演示项目的特殊之处,正是用tsgo(TypeScript 原生编译器,即 TypeScript 7 的 Native Preview)替代传统tsc做类型检查。其配置链条如下:

convex.json指定编译器(见 convex.json):

{ "typescriptCompiler": "tsgo", "$schema": "https://raw.githubusercontent.com/get-convex/convex-backend/refs/heads/main/npm-packages/convex/schemas/convex.schema.json" }

package.json声明 tsgo 依赖(见 package.json):

{ "name": "tsgo-test", "version": "0.0.0", "scripts": { "build": "tsgo --noEmit -p convex/tsconfig.json" }, "dependencies": { "convex": "workspace:*" }, "devDependencies": { "@typescript/native-preview": "~7.0.0-dev.20251205.1" } }

这里@typescript/native-preview就是 tsgo 的 npm 发行包;build脚本直接以tsgo --noEmit -p convex/tsconfig.json方式对函数目录做纯类型检查(不产出文件),因此该脚本也可作为 CI 中独立于 Convex CLI 的类型检查步骤。仓库的 turbo.json 进一步注明该任务的outputs为空,即"只检查、无产物"。

③ CLI 如何定位 tsgo 可执行文件:在 typecheck.ts 的findTypeScriptCompilerPath中,tsgo会依次查找node_modules/@typescript/native-preview/bin/tsgobin/tsgo.js两个候选路径;tsc则会兼容 TypeScript 6/7 并存的场景,依次查找node_modules/@typescript/native/bin/tscnode_modules/typescript/bin/tsc。若找不到编译器二进制,CLI 会以cantTypeCheck结果降级处理。

④ 版本兼容性注意typescriptCompiler字段目前在convex.jsonschema 中已被标记为deprecated(见 convex.schema.json 与 CHANGELOG.md)。原因是 TypeScript 7 正式发布后,Convex CLI 会自动探测并选用原生编译器,无需再显式配置;但tsgo-test这类依赖 Native Preview 开发版的旧项目仍可通过该字段保持显式指定,两者兼容。

4.4 函数目录的 tsconfig.json 要点

convex/tsconfig.json描述了函数运行环境的 TypeScript 配置,其注释明确区分了"可修改"与"必需"两组选项(见 tsconfig.json):

  • 可自由修改allowJsstrictmoduleResolution: "Bundler"jsxskipLibCheckallowSyntheticDefaultImports
  • Convex 必需(勿改)target: "ESNext"lib: ["ES2023", "dom"]forceConsistentCasingInFileNamesmodule: "ESNext"isolatedModulesnoEmit
  • include/excludeinclude: ["./**/*"]覆盖全部函数源码;exclude: ["./_generated"]排除自动生成目录,避免与手工源码重复检查。

五、从模板到生产:函数开发的最佳实践要点

综合官方模板(README.md)与仓库实现,可以把 Convex 函数开发的关键实践总结为以下几条:

  1. 始终为args声明 Validator:这是客户端输入的第一道防线,也是客户端类型推导的数据源,空参数也应显式写args: {}(参考hello函数);
  2. 查询逻辑尽量收敛到 query,写入逻辑收敛到 mutation:query 只读、可订阅、可被自动缓存,mutation 原子写入,二者职责分离能让 UI 保持实时一致;
  3. 服务端完成数据加工:过滤敏感字段、聚合、派生计算放在 handler 中,而不是让客户端拿到全量数据;
  4. convex/下的 README 模板当作速查手册:模板中 query/mutation 的完整骨架、React 调用示例、CLI 命令提示,覆盖了 80% 的日常开发场景;
  5. 用 tsgo/tsc 做独立类型检查:可将tsgo --noEmit -p convex/tsconfig.json(或tsc等价命令)接入 CI,与npx convex deploy内置的类型检查形成双保险。

六、参考资料

  • 官方函数目录模板:README.md
  • 最小 query 函数实现:example.ts
  • 自动生成的类型绑定:server.d.ts、api.d.ts
  • 编译器与工程配置:convex.json、tsconfig.json、package.json
  • CLI 类型检查实现:typecheck.ts
  • typescriptCompiler配置项 schema:convex.schema.json
  • 数据库
  • 后端

【免费下载链接】convex-backend

The open-source reactive database for app developers

项目地址:https://gitcode.com/gh_mirrors/co/convex-backend
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/24 9:13:54

摘要写得像真的就够了?给 Agent 一份能落到原文的文献卡片

做产品调研的时候&#xff0c;最危险的一种文献综述是&#xff1a;每一句都像论文里的话&#xff0c;却没有一句能落到原文。 Agent 写得越流畅&#xff0c;读者越容易跳过核查。尤其当问题横跨医学、教育、材料这些不同领域时&#xff0c;同一个词在不同论文里可能指完全不同的…

作者头像 李华
网站建设 2026/9/24 9:12:39

海外仓WMS盘点功能设计:从流程到避坑的实战复盘

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/24 9:05:42

Cursor账号受限之后:AI编程工具的冗余工作流迁移指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/24 9:04:04

ESP32 Wi-Fi信号差?一根导线提升7dBm的改造方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/24 8:57:15

基于PZEM-004T与Raspberry Pi的Modbus RTU全屋能源监测系统实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华