convex-expert-2025
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseConvex Expert Guide - December 2025
Convex后端开发专家指南 - 2025年12月
What is Convex?
什么是Convex?
Convex is a full-stack TypeScript backend platform with:
- Reactive database - Real-time subscriptions, automatic cache invalidation
- Serverless functions - Queries, mutations, actions with TypeScript
- ACID transactions - Every mutation is a transaction, automatic conflict resolution
- Type safety - End-to-end types from schema to React hooks
- Built-in features - Auth, file storage, scheduling, vector search, workflows
Convex是一个全栈TypeScript后端平台,具备以下特性:
- 响应式数据库 - 实时订阅、自动缓存失效
- 无服务器函数 - 基于TypeScript的查询、变更、操作
- ACID事务 - 每个变更都是一次事务,自动冲突解决
- 类型安全 - 从模式到React Hooks的端到端类型支持
- 内置功能 - 身份验证、文件存储、调度、向量搜索、工作流
Function Types
函数类型
| Type | Purpose | Database | External APIs | Deterministic |
|---|---|---|---|---|
| Read data | ✅ Read | ❌ | ✅ Required |
| Write data | ✅ Read/Write | ❌ | ✅ Required |
| Side effects | Via | ✅ | ❌ |
| HTTP endpoints | Via ctx | ✅ | ❌ |
| 类型 | 用途 | 数据库访问 | 外部API调用 | 是否确定性 |
|---|---|---|---|---|
| 读取数据 | ✅ 支持读取 | ❌ 不支持 | ✅ 必须是确定性 |
| 写入数据 | ✅ 支持读写 | ❌ 不支持 | ✅ 必须是确定性 |
| 处理副作用 | 通过 | ✅ 支持 | ❌ 非确定性 |
| HTTP端点 | 通过ctx支持 | ✅ 支持 | ❌ 非确定性 |
Quick Patterns
快速示例
Schema Definition
模式定义
typescript
// convex/schema.ts
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";
export default defineSchema({
users: defineTable({
name: v.string(),
email: v.string(),
role: v.union(v.literal("admin"), v.literal("user")),
profileId: v.optional(v.id("profiles")),
})
.index("by_email", ["email"])
.index("by_role", ["role"]),
messages: defineTable({
authorId: v.id("users"),
body: v.string(),
channel: v.string(),
})
.index("by_channel", ["channel"])
.searchIndex("search_body", { searchField: "body" }),
});typescript
// convex/schema.ts
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";
export default defineSchema({
users: defineTable({
name: v.string(),
email: v.string(),
role: v.union(v.literal("admin"), v.literal("user")),
profileId: v.optional(v.id("profiles")),
})
.index("by_email", ["email"])
.index("by_role", ["role"]),
messages: defineTable({
authorId: v.id("users"),
body: v.string(),
channel: v.string(),
})
.index("by_channel", ["channel"])
.searchIndex("search_body", { searchField: "body" }),
});Query
查询函数
typescript
// convex/messages.ts
import { query } from "./_generated/server";
import { v } from "convex/values";
export const list = query({
args: { channel: v.string(), limit: v.optional(v.number()) },
handler: async (ctx, args) => {
return await ctx.db
.query("messages")
.withIndex("by_channel", (q) => q.eq("channel", args.channel))
.order("desc")
.take(args.limit ?? 50);
},
});typescript
// convex/messages.ts
import { query } from "./_generated/server";
import { v } from "convex/values";
export const list = query({
args: { channel: v.string(), limit: v.optional(v.number()) },
handler: async (ctx, args) => {
return await ctx.db
.query("messages")
.withIndex("by_channel", (q) => q.eq("channel", args.channel))
.order("desc")
.take(args.limit ?? 50);
},
});Mutation
变更函数
typescript
// convex/messages.ts
import { mutation } from "./_generated/server";
import { v } from "convex/values";
export const send = mutation({
args: { body: v.string(), channel: v.string() },
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity();
if (!identity) throw new Error("Unauthorized");
return await ctx.db.insert("messages", {
authorId: identity.subject,
body: args.body,
channel: args.channel,
});
},
});typescript
// convex/messages.ts
import { mutation } from "./_generated/server";
import { v } from "convex/values";
export const send = mutation({
args: { body: v.string(), channel: v.string() },
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity();
if (!identity) throw new Error("Unauthorized");
return await ctx.db.insert("messages", {
authorId: identity.subject,
body: args.body,
channel: args.channel,
});
},
});Action
操作函数
typescript
// convex/ai.ts
import { action } from "./_generated/server";
import { v } from "convex/values";
import { api, internal } from "./_generated/api";
export const generateResponse = action({
args: { prompt: v.string(), threadId: v.id("threads") },
handler: async (ctx, args) => {
// Call external API
const response = await fetch("https://api.openai.com/v1/chat/completions", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.OPENAI_API_KEY}` },
body: JSON.stringify({ model: "gpt-4", messages: [{ role: "user", content: args.prompt }] }),
});
const data = await response.json();
// Write result via mutation
await ctx.runMutation(internal.messages.saveAIResponse, {
threadId: args.threadId,
content: data.choices[0].message.content,
});
},
});typescript
// convex/ai.ts
import { action } from "./_generated/server";
import { v } from "convex/values";
import { api, internal } from "./_generated/api";
export const generateResponse = action({
args: { prompt: v.string(), threadId: v.id("threads") },
handler: async (ctx, args) => {
// 调用外部API
const response = await fetch("https://api.openai.com/v1/chat/completions", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.OPENAI_API_KEY}` },
body: JSON.stringify({ model: "gpt-4", messages: [{ role: "user", content: args.prompt }] }),
});
const data = await response.json();
// 通过变更函数写入结果
await ctx.runMutation(internal.messages.saveAIResponse, {
threadId: args.threadId,
content: data.choices[0].message.content,
});
},
});React Integration
React集成
typescript
// app/page.tsx
"use client";
import { useQuery, useMutation } from "convex/react";
import { api } from "../convex/_generated/api";
export default function Chat() {
const messages = useQuery(api.messages.list, { channel: "general" });
const sendMessage = useMutation(api.messages.send);
if (messages === undefined) return <div>Loading...</div>;
return (
<div>
{messages.map((msg) => <p key={msg._id}>{msg.body}</p>)}
<button onClick={() => sendMessage({ body: "Hello!", channel: "general" })}>
Send
</button>
</div>
);
}typescript
// app/page.tsx
"use client";
import { useQuery, useMutation } from "convex/react";
import { api } from "../convex/_generated/api";
export default function Chat() {
const messages = useQuery(api.messages.list, { channel: "general" });
const sendMessage = useMutation(api.messages.send);
if (messages === undefined) return <div>加载中...</div>;
return (
<div>
{messages.map((msg) => <p key={msg._id}>{msg.body}</p>)}
<button onClick={() => sendMessage({ body: "Hello!", channel: "general" })}>
发送
</button>
</div>
);
}Reference Files
参考文档
Load based on task:
- Functions, schema, validators: See functions-and-schema.md
- Database queries, indexes, pagination: See database-patterns.md
- React hooks, Next.js, TanStack Query: See client-integration.md
- Convex Auth, Clerk, Auth0: See authentication.md
- File storage, cron, scheduling: See file-storage-scheduling.md
- AI agents, vector search, workflows: See ai-agents-workflows.md
- Components, security, optimization: See components-best-practices.md
根据任务需求选择查看:
- 函数、模式、验证器:查看functions-and-schema.md
- 数据库查询、索引、分页:查看database-patterns.md
- React Hooks、Next.js、TanStack Query:查看client-integration.md
- Convex Auth、Clerk、Auth0:查看authentication.md
- 文件存储、定时任务、调度:查看file-storage-scheduling.md
- AI Agent、向量搜索、工作流:查看ai-agents-workflows.md
- 组件、安全、优化:查看components-best-practices.md
Core Concepts
核心概念
Reactivity
响应性
- creates a WebSocket subscription
useQuery - UI updates automatically when data changes
- No manual cache invalidation needed
- All subscriptions update atomically
- 会创建WebSocket订阅
useQuery - 数据变化时UI自动更新
- 无需手动处理缓存失效
- 所有订阅都会原子性更新
Determinism
确定性
- Queries and mutations must be deterministic
- No ,
Math.random(), orDate.now()allowedfetch - Use instead of
_creationTimeDate.now() - Actions are the escape hatch for non-deterministic work
- 查询和变更函数必须是确定性的
- 不允许使用、
Math.random()或Date.now()fetch - 用替代
_creationTimeDate.now() - 操作函数是处理非确定性逻辑的出口
Transactions
事务
- Every mutation is an ACID transaction
- Automatic optimistic concurrency control
- Retries on conflicts
- No BEGIN/COMMIT needed
- 每个变更函数都是ACID事务
- 自动乐观并发控制
- 冲突时自动重试
- 无需手动编写BEGIN/COMMIT
Type Safety
类型安全
- Schema generates TypeScript types
- for document types
Doc<"tableName"> - for ID types
Id<"tableName"> - End-to-end type checking from schema to React
- 模式会自动生成TypeScript类型
- 使用表示文档类型
Doc<"tableName"> - 使用表示ID类型
Id<"tableName"> - 从模式到React实现端到端类型检查
Validator Reference
验证器参考
| Validator | TypeScript Type | Example |
|---|---|---|
| | |
| | |
| | |
| | |
| | Document ID |
| | |
| | |
| | Optional field |
| | Union type |
| | Exact value |
| | Any value |
| | Binary data |
| | Key-value map |
| 验证器 | TypeScript类型 | 示例 |
|---|---|---|
| | |
| | |
| | |
| | |
| | 文档ID |
| | |
| | |
| | 可选字段 |
| | 联合类型 |
| | 精确值 |
| | 任意值 |
| | 二进制数据 |
| | 键值对映射 |
Decision Framework
决策框架
When to Use Each Function Type
函数类型选择指南
Need to read data?
├─ Yes → query
│ └─ Need real-time updates? → useQuery (React)
│ └─ One-time fetch? → fetchQuery (Server)
└─ No
└─ Need to write data?
├─ Yes → mutation
│ └─ Also need external API? → mutation + scheduler.runAfter(0, action)
└─ No
└─ Need external API? → action
└─ Need durability? → Workflow component需要读取数据?
├─ 是 → 使用query
│ └─ 需要实时更新? → 使用useQuery(React端)
│ └─ 一次性获取? → 使用fetchQuery(服务端)
└─ 否
└─ 需要写入数据?
├─ 是 → 使用mutation
│ └─ 同时需要调用外部API? → mutation + scheduler.runAfter(0, action)
└─ 否
└─ 需要调用外部API? → 使用action
└─ 需要持久性? → 使用工作流组件Internal vs Public Functions
内部函数 vs 公共函数
| Use Case | Function Type |
|---|---|
| Client can call | |
| Backend only | |
| Scheduled work | Internal functions |
| Security-sensitive | Internal functions |
| 使用场景 | 函数类型 |
|---|---|
| 客户端可直接调用 | |
| 仅后端可用 | |
| 定时任务 | 内部函数 |
| 安全敏感逻辑 | 内部函数 |
Project Structure
项目结构
my-app/
├── convex/
│ ├── _generated/ # Auto-generated (don't edit)
│ │ ├── api.d.ts
│ │ ├── api.js
│ │ ├── dataModel.d.ts
│ │ └── server.d.ts
│ ├── schema.ts # Database schema
│ ├── auth.ts # Auth configuration
│ ├── users.ts # User functions
│ ├── messages.ts # Message functions
│ ├── crons.ts # Scheduled jobs
│ ├── http.ts # HTTP endpoints
│ └── model/ # Business logic (recommended)
│ ├── users.ts
│ └── messages.ts
├── app/ # Next.js app
│ ├── ConvexClientProvider.tsx
│ └── page.tsx
└── .env.local
└── NEXT_PUBLIC_CONVEX_URL=...my-app/
├── convex/
│ ├── _generated/ # 自动生成(请勿编辑)
│ │ ├── api.d.ts
│ │ ├── api.js
│ │ ├── dataModel.d.ts
│ │ └── server.d.ts
│ ├── schema.ts # 数据库模式文件
│ ├── auth.ts # 身份验证配置
│ ├── users.ts # 用户相关函数
│ ├── messages.ts # 消息相关函数
│ ├── crons.ts # 定时任务
│ ├── http.ts # HTTP端点
│ └── model/ # 业务逻辑(推荐目录)
│ ├── users.ts
│ └── messages.ts
├── app/ # Next.js应用目录
│ ├── ConvexClientProvider.tsx
│ └── page.tsx
└── .env.local
└── NEXT_PUBLIC_CONVEX_URL=...CLI Commands
CLI命令
bash
undefinedbash
undefinedInitialize Convex in project
在项目中初始化Convex
npx convex init
npx convex init
Start development server (watches for changes)
启动开发服务器(监听文件变化)
npx convex dev
npx convex dev
Deploy to production
部署到生产环境
npx convex deploy
npx convex deploy
Open dashboard
打开控制台
npx convex dashboard
npx convex dashboard
Run a function manually
手动运行函数
npx convex run messages:list '{"channel": "general"}'
npx convex run messages:list '{"channel": "general"}'
Import data
导入数据
npx convex import --table messages data.json
npx convex import --table messages data.json
Export data
导出数据
npx convex export --path ./backup
undefinednpx convex export --path ./backup
undefinedEnvironment Variables
环境变量
bash
undefinedbash
undefined.env.local (development)
.env.local(开发环境)
NEXT_PUBLIC_CONVEX_URL=https://your-project.convex.cloud
NEXT_PUBLIC_CONVEX_URL=https://your-project.convex.cloud
Set in Convex dashboard for production
在Convex控制台配置(生产环境)
OPENAI_API_KEY=sk-...
CLERK_SECRET_KEY=sk_...
Access in functions:
```typescript
const apiKey = process.env.OPENAI_API_KEY;OPENAI_API_KEY=sk-...
CLERK_SECRET_KEY=sk_...
在函数中访问环境变量:
```typescript
const apiKey = process.env.OPENAI_API_KEY;