数据库

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.tsBetter Auth 用户、会话、账号和验证表
src/lib/db/schema/payment.ts支付与订阅表
drizzle.config.tsSQLite schema 路径和可选的 d1-http 工具配置
drizzle/已生成、审查的 SQL 迁移和 Drizzle 元数据
wrangler.jsonc本地/生产 DB 绑定与迁移目录

请求期间读取 D1 绑定

src/lib/db/index.tscloudflare: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

pnpm db:generate

该命令比较 schema 与 Drizzle 元数据,并在 drizzle/ 写入迁移,不需要连接 D1。

审查并提交迁移

检查生成 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。

相关页面

On this page