关于 drizzle-orm-d1
为使用 Cloudflare D1 的开发者提供 Drizzle ORM 配置与实践参考,涵盖类型安全查询、数据库模式和迁移流程。内容还说明 D1 特有的事务、外键和部署问题的规避方式。
主要功能
- 01TypeScript 类型安全查询
- 02定义 SQLite 数据库模式
- 03生成并应用迁移
- 04使用 D1 batch API
- 05排查 D1 集成常见问题
适用场景
- 01在 Cloudflare Worker 中查询 D1
- 02通过 Drizzle Kit 管理数据库迁移
- 03本地测试迁移后应用到远程数据库
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jackspace/ claudeskillz --skill drizzle-orm-d1安装后在 Claude Code 中输入/drizzle-orm-d1确认已加载。
附带文件README.mdSKILL.json
详细介绍
适用于 Cloudflare D1 的 Drizzle ORM
状态:已可用于生产环境 ✅ 上次更新:2025-10-24 最新版本:[email protected],[email protected] 依赖项:cloudflare-d1,cloudflare-worker-base
快速入门(10 分钟)
1. 安装 Drizzle
npm install drizzle-orm
npm install -D drizzle-kit
# Or with pnpm
pnpm add drizzle-orm
pnpm add -D drizzle-kit
为什么选择 Drizzle?
- 具备完整 TypeScript 类型推断的类型安全查询
- 类 SQL 语法(没有魔法,没有抽象层开销)
- 已为无服务器环境做好准备(与 D1 完美配合)
- 零依赖(数据库驱动程序除外)
- 出色的开发体验,支持 IDE 自动补全
- 可与 Wrangler 配合使用的迁移
2. 配置 Drizzle Kit
在项目根目录中创建 drizzle.config.ts:
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
schema: './src/db/schema.ts',
out: './migrations',
dialect: 'sqlite',
driver: 'd1-http',
dbCredentials: {
accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
databaseId: process.env.CLOUDFLARE_DATABASE_ID!,
token: process.env.CLOUDFLARE_D1_TOKEN!,
},
});
重要:
dialect: 'sqlite'- D1 基于 SQLitedriver: 'd1-http'- 通过 HTTP API 访问远程数据库- 使用环境变量存储凭据(切勿提交!)
3. 配置 Wrangler
更新 wrangler.jsonc:
{
"name": "my-worker",
"main": "src/index.ts",
"compatibility_date": "2025-10-11",
"d1_databases": [
{
"binding": "DB",
"database_name": "my-database",
"database_id": "your-database-id",
"preview_database_id": "local-db",
"migrations_dir": "./migrations" // ← Points to Drizzle migrations!
}
]
}
为什么这很重要:
migrations_dir告诉 Wrangler 在哪里查找 SQL 迁移文件- Drizzle 会在
./migrations中生成迁移文件(来自 drizzle.config.ts 的out) - Wrangler 可使用
wrangler d1 migrations apply应用 Drizzle 生成的迁移
4. 定义架构
创建 src/db/schema.ts:
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';
import { relations } from 'drizzle-orm';
export const users = sqliteTable('users', {
id: integer('id').primaryKey({ autoIncrement: true }),
email: text('email').notNull().unique(),
name: text('name').notNull(),
createdAt: integer('created_at', { mode: 'timestamp' }).$defaultFn(() => new Date()),
});
export const posts = sqliteTable('posts', {
id: integer('id').primaryKey({ autoIncrement: true }),
title: text('title').notNull(),
content: text('content').notNull(),
authorId: integer('author_id')
.notNull()
.references(() => users.id, { onDelete: 'cascade' }),
createdAt: integer('created_at', { mode: 'timestamp' }).$defaultFn(() => new Date()),
});
// Define relations for type-safe joins
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}));
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, { fields: [posts.authorId], references: [users.id] }),
}));
要点:
- 使用
integer创建自增 ID - 日期使用带
mode: 'timestamp'的integer(D1 没有原生日期类型) - 动态默认值使用
.$defaultFn()(函数不能使用.default()) - 单独定义关系,以支持类型安全的联接
5. 生成并应用迁移
# Step 1: Generate SQL migration from schema
npx drizzle-kit generate
# Step 2: Apply to local database (for testing)
npx wrangler d1 migrations apply my-database --local
# Step 3: Apply to production database
npx wrangler d1 migrations apply my-database --remote
为什么采用此工作流:
drizzle-kit generate会在./migrations中创建带版本号的 SQL 文件- 先使用
--local标志进行本地测试 - 只有本地测试成功后才应用到生产环境
- Wrangler 会读取迁移文件并将其应用到 D1
6. 在 Worker 中查询
创建 src/index.ts:
import { drizzle } from 'drizzle-orm/d1';
import { users, posts } from './db/schema';
import { eq } from 'drizzle-orm';
export interface Env {
DB: D1Database;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const db = drizzle(env.DB);
// Type-safe select with full inference
const allUsers = await db.select().from(users);
// Select with where clause
const user = await db
.select()
.from(users)
.where(eq(users.email, '[email protected]'))
.get(); // .get() returns first result or undefined
// Insert with returning
const [newUser] = await db
.insert(users)
.values({ email: '[email protected]', name: 'New User' })
.returning();
// Update
await db
.update(users)
.set({ name: 'Updated Name' })
.where(eq(users.id, 1));
// Delete
await db
.delete(users)
.where(eq(users.id, 1));
return Response.json({ allUsers, user, newUser });
},
};
重要:
- 单条结果使用
.get()(返回第一条结果或 undefined) - 所有结果使用
.all()(返回数组) - 从
drizzle-orm导入运算符:eq、gt、lt、and、or等 - D1 支持
.returning()(返回插入或更新的行)
完整设置流程
步骤 1:安装依赖项
# Core dependencies
npm install drizzle-orm
# Dev dependencies
npm install -D drizzle-kit @cloudflare/workers-types
# Optional: For local development with SQLite
npm install -D better-sqlite3
步骤 2:环境变量
创建 .env(切勿提交!):
# Get these from Cloudflare dashboard
CLOUDFLARE_ACCOUNT_ID=your-account-id
CLOUDFLARE_DATABASE_ID=your-database-id
CLOUDFLARE_D1_TOKEN=your-api-token
获取方式:
- 账户 ID:Cloudflare 控制面板 → 账户主页 → 账户 ID
- 数据库 ID:运行
wrangler d1 create my-database(输出中包含 ID) - API Token:Cloudflare 控制面板 → 我的个人资料 → API Tokens → 创建 Token
步骤 3:项目结构
my-project/
├── drizzle.config.ts # Drizzle Kit configuration
├── wrangler.jsonc # Wrangler configuration
├── src/
│ ├── index.ts # Worker entry point
│ └── db/
│ └── schema.ts # Database schema
├── migrations/ # Generated by drizzle-kit
│ ├── meta/
│ │ └── _journal.json
│ └── 0001_initial_schema.sql
└── package.json
步骤 4:配置 TypeScript
更新 tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"lib": ["ES2022"],
"types": ["@cloudflare/workers-types"],
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true
}
}
重要规则
务必遵循
✅ 使用 drizzle-kit generate 进行迁移 - 切勿手动编写 SQL
✅ 先在本地测试迁移 - 在使用 --remote 之前务必使用 --local 标志
✅ 在架构中定义关系 - 支持类型安全的联接和嵌套查询
✅ 单条结果使用 .get() - 返回第一行或 undefined
✅ 使用 db.batch() 进行事务处理 - D1 不支持 SQL BEGIN/COMMIT
✅ 日期使用带 mode: 'timestamp' 的 integer - D1 没有原生日期类型
✅ 动态默认值使用 .$defaultFn() - 函数不能使用 .default()
✅ 在 wrangler.jsonc 中设置 migrations_dir - 指向 ./migrations
✅ 使用环境变量存储凭据 - 切勿提交 API 密钥
✅ 从 drizzle-orm 导入运算符 - eq、gt、and、or 等
切勿执行
❌ 切勿使用 SQL BEGIN TRANSACTION - D1 要求使用批处理 API(参见已知问题 #1)
❌ 切勿混用 wrangler d1 migrations apply 和 drizzle-kit migrate - 仅使用 Wrangler
❌ 切勿在生产环境中使用 drizzle-kit push - 使用 generate + apply 工作流
❌ 切勿忘记先在本地应用迁移 - 始终使用 --local 测试
❌ 切勿提交包含硬编码凭据的 drizzle.config.ts - 使用环境变量
❌ 切勿对函数调用使用 .default() - 应改用 .$defaultFn()
❌ 切勿依赖预处理语句缓存 - D1 不像 SQLite 那样缓存(参见已知问题 #7)
❌ 切勿使用传统的事务回滚 - 在批处理中使用错误处理(参见已知问题 #8)
❌ 切勿混用 wrangler.toml 和 wrangler.jsonc - 始终使用 wrangler.jsonc(参见已知问题 #12)
已知问题预防
此技能可预防 12 个已记录的问题:
问题 #1:D1 事务错误
错误:D1_ERROR: Cannot use BEGIN TRANSACTION
来源:https://github.com/drizzle-team/drizzle-orm/issues/4212
原因:
Drizzle 会尝试使用 SQL BEGIN TRANSACTION 语句,但 Cloudflare D1 会抛出 D1_ERROR,要求改用 state.storage.transaction() API。由于 Drizzle 在 Workers 中使用绑定时会尝试使用 BEGIN TRANSACTION,用户无法绕过此错误。
预防方法: 使用 D1 的批处理 API,而非 Drizzle 的事务 API:
// ❌ DON'T: Use traditional transactions
await db.transaction(async (tx) => {
await tx.insert(users).values({ email: '[email protected]', name: 'Test' });
await tx.insert(posts).values({ title: 'Post', content: 'Content', authorId: 1 });
});
// ✅ DO: Use D1 batch API
await db.batch([
db.insert(users).values({ email: '[email protected]', name: 'Test' }),
db.insert(posts).values({ title: 'Post', content: 'Content', authorId: 1 }),
]);
模板:参见 templates/transactions.ts
问题 #2:外键约束失败
错误:FOREIGN KEY constraint failed: SQLITE_CONSTRAINT
来源:https://github.com/drizzle-team/drizzle-orm/issues/4089
原因:
为 Cloudflare D1 生成迁移时,Drizzle-ORM 会使用 PRAGMA foreign_keys = OFF; 语句,导致执行迁移失败。如果表中已有数据,并生成了新的迁移,就会因外键错误而失败。
预防方法:
- 始终在架构中定义外键,并设置恰当的级联规则:
export const posts = sqliteTable('posts', {
id: integer('id').primaryKey({ autoIncrement: true }),
authorId: integer('author_id')
.notNull()
.references(() => users.id, { onDelete: 'cascade' }), // ← Cascading deletes
});
- 确保迁移顺序正确(先迁移父表,再迁移子表)
- 在生产环境应用迁移前,先在本地测试
模板:参见 templates/schema.ts
问题 #3:生产环境中的模块导入错误
错误:Error: No such module "wrangler"
来源:https://github.com/drizzle-team/drizzle-orm/issues/4257
原因: 同时使用 OpenNext、Drizzle 和 D1 时,用户会遇到“Error: No such module 'wrangler'”错误。该代码在本地可以正常运行,但部署到 Cloudflare Workers 后会失败。这会影响部署到 Cloudflare 的 Next.js 项目。
预防方法:
- 不要在运行时代码中从
wrangler包导入 - 使用正确的 D1 导入方式:
import { drizzle } from 'drizzle-orm/d1' - 如有需要,将 Wrangler 配置为外部依赖
模板:参见 templates/cloudflare-worker-integration.ts
问题 #4:未找到 D1 绑定
错误:TypeError: Cannot read property 'prepare' of undefined 或 env.DB is undefined
原因:
wrangler.jsonc 配置缺失或不正确。代码中的绑定名称与配置中的名称不匹配。
预防方法: 确保绑定名称完全一致:
// wrangler.jsonc
{
"d1_databases": [
{
"binding": "DB", // ← Must match env.DB in code
"database_name": "my-database",
"database_id": "your-db-id"
}
]
}
// src/index.ts
export interface Env {
DB: D1Database; // ← Must match binding name
}
export default {
async fetch(request: Request, env: Env) {
const db = drizzle(env.DB); // ← Accessing the binding
// ...
},
};
参考:参见 references/wrangler-setup.md
问题 #5:应用迁移失败
错误:Migration failed to apply: near "...": syntax error
原因: 生成的 SQL 存在语法错误、迁移冲突,或迁移应用顺序不正确。
预防方法:
- 始终先在本地测试迁移:
npx wrangler d1 migrations apply my-database --local
-
应用前检查
./migrations中生成的 SQL -
如果迁移失败,删除该迁移并重新生成:
rm -rf migrations/
npx drizzle-kit generate
参考:参见 references/migration-workflow.md
问题 #6:Schema TypeScript 推断错误
错误:Type instantiation is excessively deep and possibly infinite
原因: 关系中的复杂循环引用会导致 TypeScript 类型推断失败。
预防措施: 在关系中使用显式类型注解:
import { InferSelectModel } from 'drizzle-orm';
// Define types explicitly
export type User = InferSelectModel<typeof users>;
export type Post = InferSelectModel<typeof posts>;
// Use explicit types in relations
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}));
参考:参见 references/schema-patterns.md
问题 #7:预处理语句缓存问题
错误:查询结果过时或不正确
原因: 开发者以为 D1 会像传统 SQLite 一样缓存预处理语句,但 D1 不会在请求之间保留语句缓存。
预防措施:
始终正确使用 .all()、.get() 或 .run() 方法:
// ✅ Correct: Use .all() for arrays
const users = await db.select().from(users).all();
// ✅ Correct: Use .get() for single result
const user = await db.select().from(users).where(eq(users.id, 1)).get();
// ❌ Wrong: Don't rely on caching behavior
const stmt = db.select().from(users); // Don't reuse across requests
模板:参见 templates/prepared-statements.ts
问题 #8:事务回滚模式
错误:出错时事务未回滚
原因: D1 批处理 API 不支持传统的事务回滚。如果批次中的一条语句失败,其他语句仍可能成功。
预防措施: 通过手动清理实现错误处理:
try {
const results = await db.batch([
db.insert(users).values({ email: '[email protected]', name: 'Test' }),
db.insert(posts).values({ title: 'Post', content: 'Content', authorId: 1 }),
]);
// Both succeeded
} catch (error) {
// Manual cleanup if needed
console.error('Batch failed:', error);
// Potentially delete partially created records
}
模板:参见 templates/transactions.ts
问题 #9:TypeScript 严格模式错误
错误:tsconfig.json 中设置 strict: true 时出现类型错误
原因: Drizzle 类型可能不够严格,而 TypeScript 严格模式会捕获潜在问题。
预防措施: 使用显式返回类型和断言:
// ✅ Explicit return type
async function getUser(id: number): Promise<User | undefined> {
return await db.select().from(users).where(eq(users.id, id)).get();
}
// ✅ Type assertion when needed
const user = await db.select().from(users).where(eq(users.id, 1)).get() as User;
问题 #10:未找到 Drizzle 配置
错误:Cannot find drizzle.config.ts
原因:
文件位置错误或文件名不正确。Drizzle Kit 会在项目根目录查找 drizzle.config.ts。
预防措施:
- 文件名必须严格为
drizzle.config.ts(不能是drizzle.config.js或drizzle-config.ts) - 文件必须位于项目根目录(不能在
src/或子目录中) - 如果使用其他名称,请通过
--config标志指定:
npx drizzle-kit generate --config=custom.config.ts
问题 #11:远程与本地 D1 混淆
错误:更改未出现在本地开发环境或生产环境中
原因:
迁移被应用到了错误的数据库。开发时忘记使用 --local 标志,或在生产环境中使用了该标志。
预防措施: 始终使用一致的标志:
# Development: Always use --local
npx wrangler d1 migrations apply my-database --local
npx wrangler dev # Uses local database
# Production: Use --remote
npx wrangler d1 migrations apply my-database --remote
npx wrangler deploy # Uses remote database
参考:参见 references/migration-workflow.md
问题 #12:wrangler.toml 与 wrangler.jsonc
错误:配置未被识别,或注释导致错误
原因: 混用 TOML 和 JSON 配置格式。TOML 不支持相同形式的注释,而 JSON 不支持 TOML 语法。
预防措施:
始终使用 wrangler.jsonc:
// wrangler.jsonc (supports comments!)
{
"name": "my-worker",
// This is a comment
"d1_databases": [
{
"binding": "DB",
"database_name": "my-database"
}
]
}
不要使用:
# wrangler.toml (old format)
name = "my-worker"
参考:参见 references/wrangler-setup.md
配置文件参考
drizzle.config.ts(完整示例)
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
// Schema location (can be file or directory)
schema: './src/db/schema.ts',
// Output directory for migrations
out: './migrations',
// Database dialect
dialect: 'sqlite',
// D1 HTTP driver (for remote access)
driver: 'd1-http',
// Cloudflare credentials
dbCredentials: {
accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
databaseId: process.env.CLOUDFLARE_DATABASE_ID!,
token: process.env.CLOUDFLARE_D1_TOKEN!,
},
// Verbose output
verbose: true,
// Strict mode
strict: true,
});
wrangler.jsonc(完整示例)
{
"name": "my-worker",
"main": "src/index.ts",
"compatibility_date": "2025-10-11",
// D1 database bindings
"d1_databases": [
{
"binding": "DB",
"database_name": "my-database",
"database_id": "your-production-db-id",
"preview_database_id": "local-db",
"migrations_dir": "./migrations" // Points to Drizzle migrations
}
],
// Node.js compatibility for Drizzle
"compatibility_flags": ["nodejs_compat"]
}
package.json 脚本
{
"scripts": {
"dev": "wrangler dev",
"deploy": "wrangler deploy",
"db:generate": "drizzle-kit generate",
"db:push": "drizzle-kit push",
"db:studio": "drizzle-kit studio",
"db:migrate:local": "wrangler d1 migrations apply my-database --local",
"db:migrate:remote": "wrangler d1 migrations apply my-database --remote"
}
}
常见模式
模式 1:CRUD 操作
import { drizzle } from 'drizzle-orm/d1';
import { users } from './db/schema';
import { eq, and, or, gt, lt, like } from 'drizzle-orm';
const db = drizzle(env.DB);
// Create
const [newUser] = await db
.insert(users)
.values({ email: '[email protected]', name: 'New User' })
.returning();
// Read (all)
const allUsers = await db.select().from(users).all();
// Read (single)
const user = await db
.select()
.from(users)
.where(eq(users.id, 1))
.get();
// Read (with conditions)
const activeUsers = await db
.select()
.from(users)
.where(and(
gt(users.createdAt, new Date('2024-01-01')),
like(users.email, '%@example.com')
))
.all();
// Update
await db
.update(users)
.set({ name: 'Updated Name' })
.where(eq(users.id, 1));
// Delete
await db
.delete(users)
.where(eq(users.id, 1));
模板:参见 templates/basic-queries.ts
模式 2:关系与联接
import { drizzle } from 'drizzle-orm/d1';
import { users, posts } from './db/schema';
import { eq } from 'drizzle-orm';
const db = drizzle(env.DB, { schema: { users, posts, usersRelations, postsRelations } });
// Nested query (requires relations defined)
const usersWithPosts = await db.query.users.findMany({
with: {
posts: true,
},
});
// Manual join
const usersWithPosts2 = await db
.select({
user: users,
post: posts,
})
.from(users)
.leftJoin(posts, eq(posts.authorId, users.id))
.all();
// Filter nested queries
const userWithRecentPosts = await db.query.users.findFirst({
where: eq(users.id, 1),
with: {
posts: {
where: gt(posts.createdAt, new Date('2024-01-01')),
orderBy: [desc(posts.createdAt)],
limit: 10,
},
},
});
模板:参见 templates/relations-queries.ts
模式 3:批量操作(事务)
import { drizzle } from 'drizzle-orm/d1';
import { users, posts } from './db/schema';
const db = drizzle(env.DB);
// Batch insert
const results = await db.batch([
db.insert(users).values({ email: '[email protected]', name: 'User 1' }),
db.insert(users).values({ email: '[email protected]', name: 'User 2' }),
db.insert(users).values({ email: '[email protected]', name: 'User 3' }),
]);
// Batch with error handling
try {
const results = await db.batch([
db.insert(users).values({ email: '[email protected]', name: 'Test' }),
db.insert(posts).values({ title: 'Post', content: 'Content', authorId: 1 }),
]);
console.log('All operations succeeded');
} catch (error) {
console.error('Batch failed:', error);
// Manual cleanup if needed
}
模板:参见 templates/transactions.ts
模式 4:预处理语句
import { drizzle } from 'drizzle-orm/d1';
import { users } from './db/schema';
import { eq } from 'drizzle-orm';
const db = drizzle(env.DB);
// Prepared statement (reusable query)
const getUserById = db
.select()
.from(users)
.where(eq(users.id, sql.placeholder('id')))
.prepare();
// Execute with different parameters
const user1 = await getUserById.get({ id: 1 });
const user2 = await getUserById.get({ id: 2 });
注意:D1 不会像传统 SQLite 一样在请求之间缓存预处理语句。
模板:参见 templates/prepared-statements.ts
使用捆绑资源
脚本(scripts/)
check-versions.sh - 验证软件包版本是否为最新
./scripts/check-versions.sh
输出:
Checking Drizzle ORM versions...
✓ drizzle-orm: 0.44.7 (latest)
✓ drizzle-kit: 0.31.5 (latest)
参考资料(references/)
需要深入了解特定信息时,Claude 应加载以下资料:
- wrangler-setup.md - 完整的 Wrangler 配置指南(本地与远程、环境变量)
- schema-patterns.md - 所有 D1/SQLite 列类型、约束和索引
- migration-workflow.md - 完整的迁移工作流(生成、测试、应用)
- query-builder-api.md - 完整的 Drizzle 查询构建器 API 参考
- common-errors.md - 全部 12 个错误及详细解决方案
- links-to-official-docs.md - 官方文档链接汇总
加载时机:
- 用户询问特定列类型 → 加载 schema-patterns.md
- 用户遇到迁移错误 → 加载 migration-workflow.md + common-errors.md
- 用户需要完整 API 参考 → 加载 query-builder-api.md
高级主题
TypeScript 类型推断
import { InferSelectModel, InferInsertModel } from 'drizzle-orm';
import { users } from './db/schema';
// Infer types from schema
export type User = InferSelectModel<typeof users>;
export type NewUser = InferInsertModel<typeof users>;
// Usage
const user: User = await db.select().from(users).where(eq(users.id, 1)).get();
const newUser: NewUser = {
email: '[email protected]',
name: 'Test User',
// createdAt is optional (has default)
};
迁移工作流最佳实践
开发:
- 在
src/db/schema.ts中修改架构 - 生成迁移:
npm run db:generate - 检查
./migrations中生成的 SQL - 在本地应用:
npm run db:migrate:local - 在本地开发环境中测试:
npm run dev - 将迁移文件提交到 Git
生产环境:
- 部署代码:
npm run deploy - 应用迁移:
npm run db:migrate:remote - 在生产环境中验证
参考:参见 references/migration-workflow.md
处理日期
D1/SQLite 没有原生日期类型。使用带有时间戳模式的整数:
export const events = sqliteTable('events', {
id: integer('id').primaryKey({ autoIncrement: true }),
// ✅ Use integer with timestamp mode
createdAt: integer('created_at', { mode: 'timestamp' }).$defaultFn(() => new Date()),
// ❌ Don't use text for dates
// createdAt: text('created_at'),
});
// Query with date comparisons
const recentEvents = await db
.select()
.from(events)
.where(gt(events.createdAt, new Date('2024-01-01')))
.all();
依赖项
必需:
[email protected]- ORM 运行时[email protected]- 用于迁移的 CLI 工具
可选:
[email protected]- 用于本地 SQLite 开发@cloudflare/[email protected]- TypeScript 类型
技能:
- cloudflare-d1 - D1 数据库创建和原始 SQL 查询
- cloudflare-worker-base - Worker 项目结构和 Hono 配置
官方文档
- Drizzle ORM:https://orm.drizzle.team/
- Drizzle with D1:https://orm.drizzle.team/docs/connect-cloudflare-d1
- Drizzle Kit:https://orm.drizzle.team/docs/kit-overview
- Drizzle Migrations:https://orm.drizzle.team/docs/migrations
- GitHub:https://github.com/drizzle-team/drizzle-orm
- Cloudflare D1:https://developers.cloudflare.com/d1/
- Wrangler D1 Commands:https://developers.cloudflare.com/workers/wrangler/commands/#d1
- Context7 Library:
/drizzle-team/drizzle-orm-docs
软件包版本(2025-10-24 验证)
{
"dependencies": {
"drizzle-orm": "^0.44.7"
},
"devDependencies": {
"drizzle-kit": "^0.31.5",
"@cloudflare/workers-types": "^4.20251014.0",
"better-sqlite3": "^12.4.1"
}
}
生产环境示例
此技能基于以下生产环境实践:
- Cloudflare Workers + D1:无服务器边缘数据库
- Drizzle ORM:生产应用中使用的类型安全 ORM
- 错误:0(已防止全部 12 个已知问题)
- 验证:✅ 完整的博客示例(用户、文章、评论)
故障排除
问题:D1_ERROR: Cannot use BEGIN TRANSACTION
解决方案:使用 db.batch() 代替 db.transaction()(参见已知问题 #1)
问题:迁移期间外键约束失败
解决方案:定义级联删除,并确保迁移顺序正确(参见已知问题 #2)
问题:迁移未应用
解决方案:先使用 --local 标志进行本地测试,并检查生成的 SQL(参见已知问题 #5)
问题:关系相关的 TypeScript 类型错误
解决方案:使用 InferSelectModel 添加显式类型注解(参见已知问题 #6)
问题:env.DB 未定义
解决方案:检查 wrangler.jsonc 中的绑定名称是否与代码一致(参见已知问题 #4)
完整设置清单
- 已安装 drizzle-orm 和 drizzle-kit
- 已在项目根目录创建 drizzle.config.ts
- 已设置环境变量(CLOUDFLARE_ACCOUNT_ID 等)
- 已使用 D1 绑定和 migrations_dir 更新 wrangler.jsonc
- 已在 src/db/schema.ts 中定义架构
- 已使用
drizzle-kit generate生成首个迁移 - 已使用
wrangler d1 migrations apply --local在本地应用迁移 - 已在 Worker 中测试查询
- 已使用
--remote将迁移应用到生产环境 - 已使用
wrangler deploy部署 Worker - 已验证所有软件包版本均正确
- 已为常见任务设置 npm 脚本
有疑问?遇到问题?
- 查看
references/common-errors.md,了解全部 12 个已知问题 - 验证设置流程中的所有步骤
- 查看官方文档:https://orm.drizzle.team/docs/connect-cloudflare-d1
- 确保已创建 D1 数据库并配置好绑定
节省 Token:相比手动设置约 60% 错误防范:100%(已记录并防范全部 12 个已知问题) 已准备好用于生产环境! ✅