Convex:一条命令给 Expo 应用接上后端
Expo 和 Convex 把「应用有了、后端还没有」这段路压成一条命令:自动建项目、写环境变量、生成类型,之后 useQuery 会自己跟着数据更新。
中文
复制

本文是 Convex 开发者社区负责人 Wayne Sutton 的客座文章。一条 EAS CLI 命令,就能给你的 Expo 应用配好并接上一个响应式的、类型完整的 Convex 后端:不用单独注册、不用开一堆后台面板、不用来回复制 token。
我出去跟开发者聊天时,经常看到这样一个瞬间:应用你已经做完了,页面能跑、导航很顺、演示效果也很好;然后有人问了一句「数据存在哪儿」,你才想起来后端还没做。于是你去挑一个数据库、在某个地方注册账号、建项目、从面板里复制部署 URL、粘进环境配置、再为构建配置重复设置一遍同一个变量,然后在下一个跑这个项目的人踩坑之前祈祷自己全都填对了。
Expo 的人跟我说过,「我有一个应用」和「我有一个带后端的应用」之间这段路,多年来一直是 Expo 体验里最大的一块缺口。所以我们和他们一起把它补上了。现在你可以用一条命令,把 Convex(我所在的响应式数据库与后端平台)接到你的 Expo 应用上:
eas integrations:convex:connect
这条命令会替你把开通和接线全做完:引导流程里没有单独的 Convex 注册、没有 OAuth 跳转、不用在两个面板之间来回切。最后你得到的是一个类型完整、响应式的后端,以及一份已经配好本地开发和构建的环境变量。这是我最得意的一点:它感觉不像往项目里硬塞一个第三方服务,更像一个本来就在那儿的特性。
Convex 给了你什么
Convex 是一个后端平台,包含实时数据库、服务端函数、文件存储、搜索、定时调度,以及类型安全的客户端库。你用 TypeScript 写后端函数,客户端订阅它们;底层数据一变,你的 useQuery 结果会自己更新——不用手动重新拉取、不用写缓存失效代码、不用手写 REST 接口。
类型是从你的后端代码生成的,所以组件里调用的查询和你写在 convex/ 目录里的函数始终同步:你在服务端改掉一个字段名,TypeScript 会告诉你客户端哪里崩。我在 meetup 上见过很多开发者第一次撞上这一点,通常也就是他们身体前倾、开始认真听的那一刻。
什么时候该用它
当你的 Expo 应用需要存取「用户正看着的时候还在变」的数据时,就该用它:聊天、在线状态、实时比分、协作清单——任何「数据一变界面就该立刻更新」的场景。Convex 的订阅模型很适合这种形态,而且你不需要自己架 WebSocket 或者写轮询。
在项目早期,它也是最快的一条路——这是我最在意的一拨人:你有一个想法和一个 Expo 项目,想今天就有一个真后端,而不是一周之后。一条命令就能到那儿,之后你可以按需长成函数、调度和文件存储。
如果你的应用只是包在现有内部 API 外面的一层薄客户端,或者数据完全是静态的,那你大概不需要它。真正需要实时数据的时候,才伸手拿它。
怎么把 Convex 接到 Expo 应用上
你需要一个 Expo 账号、装好 EAS CLI(npm install -g eas-cli),以及一个已经用 eas init 关联到 EAS 的 Expo 项目。
在项目目录里运行:
eas integrations:convex:connect
命令会询问部署区域、项目名,必要时问你团队名——只有在需要新建一个 Convex 团队连接时才会问团队名。你也可以一次性把参数都传进去:
eas integrations:convex:connect --region aws-us-east-1 --team-name "your-team" --project-name "your-app"
这条命令在背后做了这些事:
- 用
npx expo install convex安装convex包 - 为你的 EAS 账号创建一个 Convex 团队连接,或者复用已有的
- 为这个 Expo 应用创建 Convex 项目和部署
- 把
CONVEX_DEPLOY_KEY和EXPO_PUBLIC_CONVEX_URL写进.env.local - 把
EXPO_PUBLIC_CONVEX_URL设为 EAS 环境变量,覆盖 production、preview、development 三套环境 - 往你的已验证邮箱发一封邀请,让你之后可以认领这个 Convex 团队并打开它的面板
最后一点值得单独说:邀请是异步的,所以你不用停下来等它、可以直接继续干活。马上把 Convex 的开发服务器起起来:
npx convex dev
它会(在你还没有的时候)创建本地的 convex/ 目录、生成带类型的 API 文件,并在运行期间让你的函数和部署保持同步。
然后给应用套上 provider。如果是 Expo Router 项目,就改 app/_layout.tsx:
import { ConvexProvider, ConvexReactClient } from 'convex/react';
import { Stack } from 'expo-router';
const convex = new ConvexReactClient(process.env.EXPO_PUBLIC_CONVEX_URL!, {
unsavedChangesWarning: false,
});
export default function RootLayout() {
return (
<ConvexProvider client={convex}>
<Stack />
</ConvexProvider>
);
}
在 convex/ 目录里加一个查询函数:
import { query } from './_generated/server';
export const get = query({
args: {},
handler: async ctx => {
return await ctx.db.query('tasks').collect();
},
});
再在页面里调用它:
import { api } from '@/convex/_generated/api';
import { useQuery } from 'convex/react';
import { Text, View } from 'react-native';
export default function Index() {
const tasks = useQuery(api.tasks.get);
return (
<View>
{tasks?.map(task => (
<Text key={task._id}>{task.text}</Text>
))}
</View>
);
}
在 Convex 面板里改一行数据,你设备上的列表就会更新。整个闭环就这么大。
用 agent 来配
我合作的很多开发者现在都用 AI agent 写代码,所以我也用这种方式测过。因为它只是一条命令、参数又很确定,所以当成给编码 agent 的指令非常合适。把下面这段粘给它就行:
在我的 Expo 项目里运行
eas integrations:convex:connect,开通并连接一个 Convex 后端。然后在app/_layout.tsx里加上ConvexProvider,用process.env.EXPO_PUBLIC_CONVEX_URL;在convex/目录里写一个示例tasks查询,并在首页用useQuery渲染出来。
agent 需要的东西都齐了:没有面板要点、没有密钥需要你念给它听。
管理这个集成
之后可以用几条命令查看或修改这层关联:
eas integrations:convex:project
eas integrations:convex:dashboard
eas integrations:convex:team
eas integrations:convex:team:invite
如果你用 eas integrations:convex:project:delete 或 eas integrations:convex:team:delete 解绑,EAS 只会删掉它自己那部分集成元数据,不会动 Convex 上的任何东西——你的数据和部署都留在原地。
几件要知道的事
这条命令是发邮件邀请,而不是在流程里直接把你登进 Convex,所以完整的面板访问权限要等你认领之后才有。区域在首发时可选 aws-us-east-1 和 aws-eu-west-1。另外 EXPO_PUBLIC_CONVEX_URL 按设计就是公开变量——客户端需要它才能访问你的部署;真正的密钥请放在 Convex 的环境变量或服务端函数里,不要放进应用包。
从哪儿开始
- 一条命令就开通并接好后端,环境变量已经为本地开发和构建准备好。
- 你得到一个响应式、类型完整的数据库:数据一变,
useQuery自己更新。 - 解绑是安全的:它删掉的是 EAS 的元数据,不是你的 Convex 资源。
如果你已经有 Expo 项目,先 eas init,再 eas integrations:convex:connect,咖啡还没凉你就有后端了。完整教程在使用 Convex 指南,Convex 概览则讲了文档、函数和订阅之间是怎么配合的。
做出来什么记得来告诉我。我常在 Convex Discord,也会看 Expo Discord。我很想知道它在哪儿帮你省了时间、又在哪儿卡住了你。