数据库
Cloudflare D1、Drizzle ORM、SQLite schema 与可审查迁移。
数据库层通过 Cloudflare D1(无服务器 SQLite)与 Drizzle ORM 的 D1 驱动 端到端集成。仓库包含认证和支付 schema,以及已生成的 SQL 迁移。
重要文件
| 文件 | 作用 |
|---|---|
src/lib/db/index.ts | 请求期间创建 D1 客户端并导出延迟 db 代理 |
src/lib/db/schema/auth.ts | Better Auth 用户、会话、账号和验证表 |
src/lib/db/schema/payment.ts | 支付与订阅表 |
drizzle.config.ts | SQLite schema 路径和可选的 d1-http 工具配置 |
drizzle/ | 已生成、审查的 SQL 迁移和 Drizzle 元数据 |
wrangler.jsonc | 本地/生产 DB 绑定与迁移目录 |
请求期间读取 D1 绑定
src/lib/db/index.ts 从 cloudflare:workers 导入 env,并通过
drizzle(env.DB, { schema }) 创建客户端。导出的 db 是延迟 Proxy,
因此 env.DB 在请求期间首次访问,而不是模块求值时读取。
DB 是 Cloudflare 绑定,不是连接字符串。没有 DATABASE_URL,运行时代码
也不能读取 process.env.DB。重组数据库模块时请保留请求期间的工厂/代理模式。
SQLite schema 约定
两个 schema 文件都使用 drizzle-orm/sqlite-core:
- 主键和大多数标识符使用
text;应用创建的支付 ID 通过$defaultFn调用crypto.randomUUID()。 - 布尔值使用
integer(..., { mode: "boolean" })。 - 日期使用
integer(..., { mode: "timestamp_ms" }),并通过$defaultFn提供应用侧Date默认值。 - 支付状态、服务商和计划周期等受限字符串使用 SQLite text enum,以获得 TypeScript 类型推断。
- 依赖用户的记录以
onDelete: "cascade"引用user.id。 - 唯一约束和显式索引覆盖 session token、邮箱、订单号、用户支付查询与订阅状态。
认证 schema 必须持续匹配 Better Auth 与管理员插件预期的字段。修改用户、会话、
账号或验证表时,应在同一评审中更新 src/lib/auth.ts 和适配器 schema。
迁移工作流
修改 TypeScript schema
编辑 src/lib/db/schema/ 下的文件。如果新增 schema 模块,请在
src/lib/db/index.ts 导入并展开到 Drizzle 的 schema 对象中。
审查并提交迁移
检查生成 SQL 中的破坏性变更、默认值、索引、外键和数据回填。将 SQL、
drizzle/meta 变更与 schema 代码一起提交。
应用到本地
pnpm db:migrate:local它会执行 wrangler d1 migrations apply soar-tanstack --local,目标是
Miniflare 数据库。
应用到远程
完成评审与备份后:
pnpm db:migrate它会执行 wrangler d1 migrations apply soar-tanstack --remote。如果重命名
D1 数据库,请同步修改 wrangler.jsonc 与包脚本。
不要使用 drizzle-kit push。模板有意使用由 Wrangler 应用的、经过生成、审查
与提交的迁移。
Drizzle 配置
drizzle.config.ts 设置:
export default defineConfig({
out: "./drizzle",
schema: "./src/lib/db/schema",
dialect: "sqlite",
driver: "d1-http",
// dbCredentials: Cloudflare account, database, and D1-scoped token
});HTTP 凭据只用于会连接远程 D1 的可选 Drizzle Kit 操作。配置会加载
.env.local,也可从 shell 导出。应用迁移使用上面的 Wrangler 脚本。
事务与多语句写入
D1 不支持典型长连接 SQL 数据库的交互式事务模型。不要围绕
db.transaction(async (tx) => ...) 设计产品逻辑。请准备相互独立的
Drizzle 语句并通过 db.batch([...]) 原子批量执行,外部网络调用放在批次之外。
对重试请求与 Webhook 设计幂等写入。例如支付 Webhook 会基于唯一 userId
upsert 订阅,使重复投递得到相同结果。
生产注意事项
- 先在非生产 D1 数据库应用并验证迁移。
- 破坏性迁移前导出或备份生产数据,并定义恢复流程与保留策略。
- 上线前根据实际负载核对 Cloudflare 当前的 D1 容量、查询和批处理限制。
- 让索引贴合真实查询,通过 Workers 可观测性检查慢路径与高流量路径。
- 如果启用读副本,请明确处理 D1 读取一致性;写后立即读取的流程可能需要 primary/session 一致性策略。
- 将迁移 SQL 视为应用代码:必须评审,也不要修改已应用到共享环境的迁移。
验证
-
pnpm db:generate没有报告意外 schema 漂移。 -
pnpm db:migrate:local能在全新的本地数据库成功执行。 - 注册会向本地 D1 写入用户/会话/认证记录。
- 测试结账会创建支付记录,签名 Webhook 能更新它。
- 同一份已审查迁移能应用到 staging 远程 D1。
