drizzle_orm

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Drizzle ORM for Cloudflare D1

适用于Cloudflare D1的Drizzle ORM

Status: Production Ready ✅ Last Updated: 2025-10-24 Latest Version: drizzle-orm@0.44.7, drizzle-kit@0.31.5 Dependencies: cloudflare-d1, cloudflare-worker-base

状态: 已就绪可用于生产环境 ✅ 最后更新: 2025-10-24 最新版本: drizzle-orm@0.44.7, drizzle-kit@0.31.5 依赖项: cloudflare-d1, cloudflare-worker-base

Quick Start (10 Minutes)

快速开始(10分钟)

1. Install Drizzle

1. 安装Drizzle

bash
npm install drizzle-orm
npm install -D drizzle-kit
bash
npm install drizzle-orm
npm install -D drizzle-kit

Or with pnpm

或使用pnpm

pnpm add drizzle-orm pnpm add -D drizzle-kit

**Why Drizzle?**
- Type-safe queries with full TypeScript inference
- SQL-like syntax (no magic, no abstraction overhead)
- Serverless-ready (works perfectly with D1)
- Zero dependencies (except database driver)
- Excellent DX with IDE autocomplete
- Migrations that work with Wrangler
pnpm add drizzle-orm pnpm add -D drizzle-kit

**为什么选择Drizzle?**
- 具备完整TypeScript类型推断的类型安全查询
- 类SQL语法(无黑魔法,无抽象开销)
- 支持无服务器架构(与D1完美兼容)
- 零依赖(除数据库驱动外)
- 出色的开发体验,支持IDE自动补全
- 可与Wrangler配合使用的迁移功能

2. Configure Drizzle Kit

2. 配置Drizzle Kit

Create
drizzle.config.ts
in your project root:
typescript
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!,
  },
});
CRITICAL:
  • dialect: 'sqlite'
    - D1 is SQLite-based
  • driver: 'd1-http'
    - For remote database access via HTTP API
  • Use environment variables for credentials (never commit these!)
在项目根目录创建
drizzle.config.ts
typescript
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基于SQLite构建
  • driver: 'd1-http'
    - 用于通过HTTP API远程访问数据库
  • 使用环境变量存储凭证(绝对不要提交到代码仓库!)

3. Configure Wrangler

3. 配置Wrangler

Update
wrangler.jsonc
:
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!
    }
  ]
}
Why this matters:
  • migrations_dir
    tells Wrangler where to find SQL migration files
  • Drizzle generates migrations in
    ./migrations
    (from drizzle.config.ts
    out
    )
  • Wrangler can apply Drizzle-generated migrations with
    wrangler d1 migrations apply
更新
wrangler.jsonc
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"  // ← 指向Drizzle迁移文件目录!
    }
  ]
}
配置的重要性:
  • migrations_dir
    告诉Wrangler在哪里找到SQL迁移文件
  • Drizzle会在
    ./migrations
    目录生成迁移文件(对应drizzle.config.ts中的
    out
    配置)
  • Wrangler可通过
    wrangler d1 migrations apply
    命令应用Drizzle生成的迁移

4. Define Your Schema

4. 定义数据库模式

Create
src/db/schema.ts
:
typescript
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] }),
}));
Key Points:
  • Use
    integer
    for auto-incrementing IDs
  • Use
    integer
    with
    mode: 'timestamp'
    for dates (D1 doesn't have native date type)
  • Use
    .$defaultFn()
    for dynamic defaults (not
    .default()
    for functions)
  • Define relations separately for type-safe joins
创建
src/db/schema.ts
typescript
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()),
});

// 定义关联以支持类型安全连接
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. Generate & Apply Migrations

5. 生成并应用迁移

bash
undefined
bash
undefined

Step 1: Generate SQL migration from schema

步骤1:从模式生成SQL迁移文件

npx drizzle-kit generate
npx drizzle-kit generate

Step 2: Apply to local database (for testing)

步骤2:应用到本地数据库(用于测试)

npx wrangler d1 migrations apply my-database --local
npx wrangler d1 migrations apply my-database --local

Step 3: Apply to production database

步骤3:应用到生产数据库

npx wrangler d1 migrations apply my-database --remote

**Why this workflow:**
- `drizzle-kit generate` creates versioned SQL files in `./migrations`
- Test locally first with `--local` flag
- Apply to production only after local testing succeeds
- Wrangler reads the migrations and applies them to D1
npx wrangler d1 migrations apply my-database --remote

**该工作流的优势**:
- `drizzle-kit generate`会在`./migrations`目录创建带版本号的SQL文件
- 先使用`--local`标志在本地测试
- 仅在本地测试通过后再应用到生产环境
- Wrangler会读取迁移文件并应用到D1数据库

6. Query in Your Worker

6. 在Worker中执行查询

Create
src/index.ts
:
typescript
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, 'test@example.com'))
      .get(); // .get() returns first result or undefined

    // Insert with returning
    const [newUser] = await db
      .insert(users)
      .values({ email: 'new@example.com', 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 });
  },
};
CRITICAL:
  • Use
    .get()
    for single results (returns first or undefined)
  • Use
    .all()
    for all results (returns array)
  • Import operators from
    drizzle-orm
    :
    eq
    ,
    gt
    ,
    lt
    ,
    and
    ,
    or
    , etc.
  • .returning()
    works with D1 (returns inserted/updated rows)

创建
src/index.ts
typescript
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);

    // 类型安全查询,具备完整类型推断
    const allUsers = await db.select().from(users);

    // 带WHERE条件的查询
    const user = await db
      .select()
      .from(users)
      .where(eq(users.email, 'test@example.com'))
      .get(); // .get()返回第一条结果或undefined

    // 插入数据并返回结果
    const [newUser] = await db
      .insert(users)
      .values({ email: 'new@example.com', name: 'New User' })
      .returning();

    // 更新数据
    await db
      .update(users)
      .set({ name: 'Updated Name' })
      .where(eq(users.id, 1));

    // 删除数据
    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
  • .returning()
    可与D1配合使用(返回插入/更新的行)

The Complete Setup Process

完整设置流程

Step 1: Install Dependencies

步骤1:安装依赖

bash
undefined
bash
undefined

Core dependencies

核心依赖

npm install drizzle-orm
npm install drizzle-orm

Dev dependencies

开发依赖

npm install -D drizzle-kit @cloudflare/workers-types
npm install -D drizzle-kit @cloudflare/workers-types

Optional: For local development with SQLite

可选:用于本地SQLite开发

npm install -D better-sqlite3
undefined
npm install -D better-sqlite3
undefined

Step 2: Environment Variables

步骤2:环境变量

Create
.env
(never commit this!):
bash
undefined
创建
.env
文件(绝对不要提交到代码仓库!):
bash
undefined

Get these from Cloudflare dashboard

从Cloudflare控制台获取

CLOUDFLARE_ACCOUNT_ID=your-account-id CLOUDFLARE_DATABASE_ID=your-database-id CLOUDFLARE_D1_TOKEN=your-api-token

**How to get these:**
1. Account ID: Cloudflare dashboard → Account Home → Account ID
2. Database ID: Run `wrangler d1 create my-database` (output includes ID)
3. API Token: Cloudflare dashboard → My Profile → API Tokens → Create Token
CLOUDFLARE_ACCOUNT_ID=your-account-id CLOUDFLARE_DATABASE_ID=your-database-id CLOUDFLARE_D1_TOKEN=your-api-token

**获取方式**:
1. 账户ID:Cloudflare控制台 → 账户主页 → 账户ID
2. 数据库ID:运行`wrangler d1 create my-database`(输出结果包含ID)
3. API令牌:Cloudflare控制台 → 我的资料 → API令牌 → 创建令牌

Step 3: Project Structure

步骤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
my-project/
├── drizzle.config.ts          # Drizzle Kit配置文件
├── wrangler.jsonc             # Wrangler配置文件
├── src/
│   ├── index.ts               # Worker入口文件
│   └── db/
│       └── schema.ts          # 数据库模式
├── migrations/                # 由drizzle-kit生成
│   ├── meta/
│   │   └── _journal.json
│   └── 0001_initial_schema.sql
└── package.json

Step 4: Configure TypeScript

步骤4:配置TypeScript

Update
tsconfig.json
:
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
  }
}

更新
tsconfig.json
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
  }
}

Critical Rules

关键规则

Always Do

必须遵守

Use
drizzle-kit generate
for migrations
- Never write SQL manually ✅ Test migrations locally first - Always use
--local
flag before
--remote
Define relations in schema - For type-safe joins and nested queries ✅ Use
.get()
for single results
- Returns first row or undefined ✅ Use
db.batch()
for transactions
- D1 doesn't support SQL BEGIN/COMMIT ✅ Use
integer
with
mode: 'timestamp'
for dates
- D1 doesn't have native date type ✅ Use
.$defaultFn()
for dynamic defaults
- Not
.default()
for functions ✅ Set
migrations_dir
in wrangler.jsonc
- Points to
./migrations
Use environment variables for credentials - Never commit API keys ✅ Import operators from drizzle-orm -
eq
,
gt
,
and
,
or
, etc.
使用
drizzle-kit generate
生成迁移
- 永远不要手动编写SQL ✅ 先在本地测试迁移 - 应用到远程前务必使用
--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

Never Do

绝对禁止

Never use SQL
BEGIN TRANSACTION
- D1 requires batch API (see Known Issue #1) ❌ Never mix
wrangler d1 migrations apply
and
drizzle-kit migrate
- Use Wrangler only ❌ Never use
drizzle-kit push
for production
- Use
generate
+
apply
workflow ❌ Never forget to apply migrations locally first - Always test with
--local
Never commit
drizzle.config.ts
with hardcoded credentials
- Use env vars ❌ Never use
.default()
for function calls
- Use
.$defaultFn()
instead ❌ Never rely on prepared statement caching - D1 doesn't cache like SQLite (see Known Issue #7) ❌ Never use traditional transaction rollback - Use error handling in batch (see Known Issue #8) ❌ Never mix wrangler.toml and wrangler.jsonc - Use wrangler.jsonc consistently (see Known Issue #12)

永远不要使用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)

Known Issues Prevention

已知问题预防

This skill prevents 12 documented issues:
本指南可避免12个已记录的问题:

Issue #1: D1 Transaction Errors

问题#1:D1事务错误

Error:
D1_ERROR: Cannot use BEGIN TRANSACTION
Why It Happens: Drizzle tries to use SQL
BEGIN TRANSACTION
statements, but Cloudflare D1 raises a D1_ERROR requiring use of
state.storage.transaction()
APIs instead. Users cannot work around this error as Drizzle attempts to use
BEGIN TRANSACTION
when using bindings in Workers.
Prevention: Use D1's batch API instead of Drizzle's transaction API:
typescript
// ❌ DON'T: Use traditional transactions
await db.transaction(async (tx) => {
  await tx.insert(users).values({ email: 'test@example.com', 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: 'test@example.com', name: 'Test' }),
  db.insert(posts).values({ title: 'Post', content: 'Content', authorId: 1 }),
]);
Template: See
templates/transactions.ts

错误信息:
D1_ERROR: Cannot use BEGIN TRANSACTION
问题原因: Drizzle尝试使用SQL
BEGIN TRANSACTION
语句,但Cloudflare D1会抛出D1_ERROR,要求使用
state.storage.transaction()
API替代。当在Worker中使用绑定功能时,用户无法绕过此错误。
解决方案: 使用D1的批量API替代Drizzle的事务API:
typescript
// ❌ 错误做法: 使用传统事务
await db.transaction(async (tx) => {
  await tx.insert(users).values({ email: 'test@example.com', name: 'Test' });
  await tx.insert(posts).values({ title: 'Post', content: 'Content', authorId: 1 });
});

// ✅ 正确做法: 使用D1批量API
await db.batch([
  db.insert(users).values({ email: 'test@example.com', name: 'Test' }),
  db.insert(posts).values({ title: 'Post', content: 'Content', authorId: 1 }),
]);
模板: 参见
templates/transactions.ts

Issue #2: Foreign Key Constraint Failures

问题#2:外键约束失败

Error:
FOREIGN KEY constraint failed: SQLITE_CONSTRAINT
Why It Happens: When generating migrations for Cloudflare D1, Drizzle-ORM uses the statement
PRAGMA foreign_keys = OFF;
which causes migrations to fail when executed. If tables have data and new migrations are generated, they fail with foreign key errors.
Prevention:
  1. Always define foreign keys in schema with proper cascading:
typescript
export const posts = sqliteTable('posts', {
  id: integer('id').primaryKey({ autoIncrement: true }),
  authorId: integer('author_id')
    .notNull()
    .references(() => users.id, { onDelete: 'cascade' }), // ← Cascading deletes
});
  1. Ensure correct migration order (parent tables before child tables)
  2. Test migrations locally before production
Template: See
templates/schema.ts

错误信息:
FOREIGN KEY constraint failed: SQLITE_CONSTRAINT
问题原因: 为Cloudflare D1生成迁移时,Drizzle-ORM会使用
PRAGMA foreign_keys = OFF;
语句,导致迁移执行失败。如果表中已有数据且生成新迁移,会因外键错误导致失败。
解决方案:
  1. 始终在模式中定义外键并设置正确的级联规则:
typescript
export const posts = sqliteTable('posts', {
  id: integer('id').primaryKey({ autoIncrement: true }),
  authorId: integer('author_id')
    .notNull()
    .references(() => users.id, { onDelete: 'cascade' }), // ← 级联删除
});
  1. 确保迁移顺序正确(父表在前,子表在后)
  2. 先在本地测试迁移再部署到生产环境
模板: 参见
templates/schema.ts

Issue #3: Module Import Errors in Production

问题#3:生产环境模块导入错误

Error:
Error: No such module "wrangler"
Why It Happens: When using OpenNext, Drizzle, and D1, users encounter "Error: No such module 'wrangler'" which works locally but fails when deployed to Cloudflare Workers. This affects Next.js projects deployed to Cloudflare.
Prevention:
  1. Don't import from
    wrangler
    package in runtime code
  2. Use correct D1 import:
    import { drizzle } from 'drizzle-orm/d1'
  3. Configure bundler to externalize Wrangler if needed
Template: See
templates/cloudflare-worker-integration.ts

错误信息:
Error: No such module "wrangler"
问题原因: 当使用OpenNext、Drizzle和D1时,用户会遇到"Error: No such module 'wrangler'"错误,该错误在本地正常但部署到Cloudflare Workers时失败。此问题影响部署到Cloudflare的Next.js项目。
解决方案:
  1. 不要在运行时代码中从
    wrangler
    包导入内容
  2. 使用正确的D1导入方式:
    import { drizzle } from 'drizzle-orm/d1'
  3. 如有需要,配置打包工具将Wrangler标记为外部依赖
模板: 参见
templates/cloudflare-worker-integration.ts

Issue #4: D1 Binding Not Found

问题#4:D1绑定未找到

Error:
TypeError: Cannot read property 'prepare' of undefined
or
env.DB is undefined
Why It Happens: Missing or incorrect
wrangler.jsonc
configuration. The binding name in code doesn't match the binding name in config.
Prevention: Ensure binding names match exactly:
jsonc
// wrangler.jsonc
{
  "d1_databases": [
    {
      "binding": "DB",  // ← Must match env.DB in code
      "database_name": "my-database",
      "database_id": "your-db-id"
    }
  ]
}
typescript
// 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
    // ...
  },
};
Reference: See
references/wrangler-setup.md

错误信息:
TypeError: Cannot read property 'prepare' of undefined
env.DB is undefined
问题原因:
wrangler.jsonc
配置缺失或不正确,代码中的绑定名称与配置中的绑定名称不匹配。
解决方案: 确保绑定名称完全匹配:
jsonc
// wrangler.jsonc
{
  "d1_databases": [
    {
      "binding": "DB",  // ← 必须与代码中的env.DB匹配
      "database_name": "my-database",
      "database_id": "your-db-id"
    }
  ]
}
typescript
// src/index.ts
export interface Env {
  DB: D1Database;  // ← 必须与绑定名称匹配
}

export default {
  async fetch(request: Request, env: Env) {
    const db = drizzle(env.DB);  // ← 访问绑定
    // ...
  },
};
参考: 参见
references/wrangler-setup.md

Issue #5: Migration Apply Failures

问题#5:迁移应用失败

Error:
Migration failed to apply: near "...": syntax error
Why It Happens: Syntax errors in generated SQL, conflicting migrations, or applying migrations out of order.
Prevention:
  1. Always test migrations locally first:
bash
npx wrangler d1 migrations apply my-database --local
  1. Review generated SQL in
    ./migrations
    before applying
  2. If migration fails, delete it and regenerate:
bash
rm -rf migrations/
npx drizzle-kit generate
Reference: See
references/migration-workflow.md

错误信息:
Migration failed to apply: near "...": syntax error
问题原因: 生成的SQL存在语法错误、迁移冲突或应用顺序错误。
解决方案:
  1. 始终先在本地测试迁移:
bash
npx wrangler d1 migrations apply my-database --local
  1. 应用前检查
    ./migrations
    目录中生成的SQL
  2. 如果迁移失败,删除后重新生成:
bash
rm -rf migrations/
npx drizzle-kit generate
参考: 参见
references/migration-workflow.md

Issue #6: Schema TypeScript Inference Errors

问题#6:模式TypeScript推断错误

Error:
Type instantiation is excessively deep and possibly infinite
Why It Happens: Complex circular references in relations cause TypeScript to fail type inference.
Prevention: Use explicit type annotations in relations:
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),
}));
Reference: See
references/schema-patterns.md

错误信息:
Type instantiation is excessively deep and possibly infinite
问题原因: 关联中的复杂循环引用导致TypeScript类型推断失败。
解决方案: 在关联中使用显式类型注解:
typescript
import { InferSelectModel } from 'drizzle-orm';

// 显式定义类型
export type User = InferSelectModel<typeof users>;
export type Post = InferSelectModel<typeof posts>;

// 在关联中使用显式类型
export const usersRelations = relations(users, ({ many }) => ({
  posts: many(posts),
}));
参考: 参见
references/schema-patterns.md

Issue #7: Prepared Statement Caching Issues

问题#7:预准备语句缓存问题

Error: Stale or incorrect results from queries
Why It Happens: Developers expect D1 to cache prepared statements like traditional SQLite, but D1 doesn't maintain statement caches between requests.
Prevention: Always use
.all()
,
.get()
, or
.run()
methods correctly:
typescript
// ✅ 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
Template: See
templates/prepared-statements.ts

错误现象: 查询结果陈旧或不正确
问题原因: 开发人员期望D1像传统SQLite一样缓存预准备语句,但D1不会在请求之间维护语句缓存。
解决方案: 始终正确使用
.all()
,
.get()
.run()
方法:
typescript
// ✅ 正确做法: 使用.all()获取数组
const users = await db.select().from(users).all();

// ✅ 正确做法: 使用.get()获取单条结果
const user = await db.select().from(users).where(eq(users.id, 1)).get();

// ❌ 错误做法: 不要依赖缓存行为
const stmt = db.select().from(users); // 不要跨请求复用
模板: 参见
templates/prepared-statements.ts

Issue #8: Transaction Rollback Patterns

问题#8:事务回滚模式

Error: Transaction doesn't roll back on error
Why It Happens: D1 batch API doesn't support traditional transaction rollback. If one statement in a batch fails, others may still succeed.
Prevention: Implement error handling with manual cleanup:
typescript
try {
  const results = await db.batch([
    db.insert(users).values({ email: 'test@example.com', 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
}
Template: See
templates/transactions.ts

错误现象: 发生错误时事务未回滚
问题原因: D1批量API不支持传统事务回滚。如果批量操作中的一个语句失败,其他语句可能仍会成功执行。
解决方案: 实现带手动清理的错误处理:
typescript
try {
  const results = await db.batch([
    db.insert(users).values({ email: 'test@example.com', name: 'Test' }),
    db.insert(posts).values({ title: 'Post', content: 'Content', authorId: 1 }),
  ]);
  // 两个操作都成功
} catch (error) {
  // 必要时进行手动清理
  console.error('批量操作失败:', error);
  // 可能需要删除部分创建的记录
}
模板: 参见
templates/transactions.ts

Issue #9: TypeScript Strict Mode Errors

问题#9:TypeScript严格模式错误

Error: Type errors with
strict: true
in tsconfig.json
Why It Happens: Drizzle types can be loose, and TypeScript strict mode catches potential issues.
Prevention: Use explicit return types and assertions:
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;

错误信息: tsconfig.json中设置
strict: true
时出现类型错误
问题原因: Drizzle的类型定义可能不够严格,TypeScript严格模式会捕获潜在问题。
解决方案: 使用显式返回类型和类型断言:
typescript
// ✅ 显式返回类型
async function getUser(id: number): Promise<User | undefined> {
  return await db.select().from(users).where(eq(users.id, id)).get();
}

// ✅ 必要时使用类型断言
const user = await db.select().from(users).where(eq(users.id, 1)).get() as User;

Issue #10: Drizzle Config Not Found

问题#10:未找到Drizzle配置

Error:
Cannot find drizzle.config.ts
Why It Happens: Wrong file location or incorrect file name. Drizzle Kit looks for
drizzle.config.ts
in the project root.
Prevention:
  1. File must be named exactly
    drizzle.config.ts
    (not
    drizzle.config.js
    or
    drizzle-config.ts
    )
  2. File must be in project root (not in
    src/
    or subdirectory)
  3. If using a different name, specify with
    --config
    flag:
bash
npx drizzle-kit generate --config=custom.config.ts

错误信息:
Cannot find drizzle.config.ts
问题原因: 文件位置错误或文件名不正确。Drizzle Kit会在项目根目录查找
drizzle.config.ts
解决方案:
  1. 文件名必须为
    drizzle.config.ts
    (不能是
    drizzle.config.js
    drizzle-config.ts
  2. 文件必须位于项目根目录(不能在
    src/
    或子目录中)
  3. 如果使用自定义名称,需通过
    --config
    标志指定:
bash
npx drizzle-kit generate --config=custom.config.ts

Issue #11: Remote vs Local D1 Confusion

问题#11:远程与本地D1混淆

Error: Changes not appearing in local development or production
Why It Happens: Applying migrations to the wrong database. Forgetting to use
--local
flag during development or using it in production.
Prevention: Use consistent flags:
bash
undefined
错误现象: 更改未在本地开发环境或生产环境中显示
问题原因: 将迁移应用到了错误的数据库,开发时忘记使用
--local
标志或在生产环境中使用了该标志。
解决方案: 始终使用一致的标志:
bash
undefined

Development: Always use --local

开发环境: 始终使用--local

npx wrangler d1 migrations apply my-database --local npx wrangler dev # Uses local database
npx wrangler d1 migrations apply my-database --local npx wrangler dev # 使用本地数据库

Production: Use --remote

生产环境: 使用--remote

npx wrangler d1 migrations apply my-database --remote npx wrangler deploy # Uses remote database

**Reference**: See `references/migration-workflow.md`

---
npx wrangler d1 migrations apply my-database --remote npx wrangler deploy # 使用远程数据库

**参考**: 参见`references/migration-workflow.md`

---

Issue #12: wrangler.toml vs wrangler.jsonc

问题#12:wrangler.toml vs wrangler.jsonc

Error: Configuration not recognized or comments causing errors
Why It Happens: Mixing TOML and JSON config formats. TOML doesn't support comments the same way, and JSON doesn't support TOML syntax.
Prevention: Use
wrangler.jsonc
consistently:
jsonc
// wrangler.jsonc (supports comments!)
{
  "name": "my-worker",
  // This is a comment
  "d1_databases": [
    {
      "binding": "DB",
      "database_name": "my-database"
    }
  ]
}
Not:
toml
undefined
错误现象: 配置未被识别或注释导致错误
问题原因: 混合使用TOML和JSON配置格式。TOML不支持与JSON相同的注释方式,JSON不支持TOML语法。
解决方案: 始终使用
wrangler.jsonc
jsonc
// wrangler.jsonc(支持注释!)
{
  "name": "my-worker",
  // 这是一条注释
  "d1_databases": [
    {
      "binding": "DB",
      "database_name": "my-database"
    }
  ]
}
不要使用:
toml
undefined

wrangler.toml (old format)

wrangler.toml(旧格式)

name = "my-worker"

**Reference**: See `references/wrangler-setup.md`

---
name = "my-worker"

**参考**: 参见`references/wrangler-setup.md`

---

Configuration Files Reference

配置文件参考

drizzle.config.ts (Full Example)

drizzle.config.ts(完整示例)

typescript
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,
});
typescript
import { defineConfig } from 'drizzle-kit';

export default defineConfig({
  // 模式位置(可以是文件或目录)
  schema: './src/db/schema.ts',

  // 迁移文件输出目录
  out: './migrations',

  // 数据库方言
  dialect: 'sqlite',

  // D1 HTTP驱动(用于远程访问)
  driver: 'd1-http',

  // Cloudflare凭证
  dbCredentials: {
    accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
    databaseId: process.env.CLOUDFLARE_DATABASE_ID!,
    token: process.env.CLOUDFLARE_D1_TOKEN!,
  },

  // 详细输出
  verbose: true,

  // 严格模式
  strict: true,
});

wrangler.jsonc (Full Example)

wrangler.jsonc(完整示例)

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"]
}
jsonc
{
  "name": "my-worker",
  "main": "src/index.ts",
  "compatibility_date": "2025-10-11",

  // D1数据库绑定
  "d1_databases": [
    {
      "binding": "DB",
      "database_name": "my-database",
      "database_id": "your-production-db-id",
      "preview_database_id": "local-db",
      "migrations_dir": "./migrations"  // 指向Drizzle迁移文件
    }
  ],

  // 为Drizzle启用Node.js兼容性
  "compatibility_flags": ["nodejs_compat"]
}

package.json Scripts

package.json脚本

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"
  }
}

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"
  }
}

Common Patterns

常见模式

Pattern 1: CRUD Operations

模式1:CRUD操作

typescript
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: 'new@example.com', 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));
Template: See
templates/basic-queries.ts

typescript
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);

// 创建
const [newUser] = await db
  .insert(users)
  .values({ email: 'new@example.com', name: 'New User' })
  .returning();

// 读取(所有)
const allUsers = await db.select().from(users).all();

// 读取(单条)
const user = await db
  .select()
  .from(users)
  .where(eq(users.id, 1))
  .get();

// 读取(带条件)
const activeUsers = await db
  .select()
  .from(users)
  .where(and(
    gt(users.createdAt, new Date('2024-01-01')),
    like(users.email, '%@example.com')
  ))
  .all();

// 更新
await db
  .update(users)
  .set({ name: 'Updated Name' })
  .where(eq(users.id, 1));

// 删除
await db
  .delete(users)
  .where(eq(users.id, 1));
模板: 参见
templates/basic-queries.ts

Pattern 2: Relations & Joins

模式2:关联与连接

typescript
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,
    },
  },
});
Template: See
templates/relations-queries.ts

#Drizzle ORM v1.1 - Enhanced
typescript
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 } });

// 嵌套查询(需要定义关联)
const usersWithPosts = await db.query.users.findMany({
  with: {
    posts: true,
  },
});

// 手动连接
const usersWithPosts2 = await db
  .select({
    user: users,
    post: posts,
  })
  .from(users)
  .leftJoin(posts, eq(posts.authorId, users.id))
  .all();

// 过滤嵌套查询
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

#Drizzle ORM v1.1 - 增强版

🔄 Workflow

🔄 工作流

Aşama 1: Schema Definition

步骤1:模式定义

  • Types:
    text
    ,
    integer
    gibi doğru veri tiplerini seç (SQLite/D1 uyumlu).
  • Constraints:
    notNull()
    ,
    unique()
    ,
    primaryKey()
    kısıtlarını tanımla.
  • Relations: Tablolar arası ilişkileri
    relations()
    fonksiyonu ile belirt.
  • 类型: 选择
    text
    integer
    等兼容SQLite/D1的正确数据类型。
  • 约束: 定义
    notNull()
    unique()
    primaryKey()
    等约束。
  • 关联: 使用
    relations()
    函数定义表之间的关联。

Aşama 2: Migration Lifecycle

步骤2:迁移生命周期

  • Generate:
    drizzle-kit generate
    ile SQL oluştur.
  • Review: Oluşan
    .sql
    dosyasını manuel kontrol et (Veri kaybı riski?).
  • Apply:
    wrangler d1 migrations apply --local
    ile önce lokalde test et.
  • 生成: 使用
    drizzle-kit generate
    生成SQL。
  • 检查: 手动检查生成的
    .sql
    文件(是否有数据丢失风险?)。
  • 应用: 先使用
    wrangler d1 migrations apply --local
    在本地测试。

Aşama 3: Query Implementation

步骤3:查询实现

  • Queries:
    .select().from()
    ile type-safe sorgular yaz.
  • Performance: N+1 sorununu
    include
    veya
    with
    kullanarak önle.
  • Batch: Toplu işlemleri
    db.batch([])
    içine al.
  • 查询: 使用
    .select().from()
    编写类型安全查询。
  • 性能: 使用
    include
    with
    避免N+1问题。
  • 批量: 将批量操作放入
    db.batch([])
    中执行。

Kontrol Noktaları

检查点

AşamaDoğrulama
1
migrations
klasörü versiyon kontrolünde (Git) mi?
2Üretim ortamına (
--remote
) geçmeden önce
--local
test edildi mi?
3Hassas veriler
.env
üzerinden mi okunuyor?
typescript
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: 'user1@example.com', name: 'User 1' }),
  db.insert(users).values({ email: 'user2@example.com', name: 'User 2' }),
  db.insert(users).values({ email: 'user3@example.com', name: 'User 3' }),
]);

// Batch with error handling
try {
  const results = await db.batch([
    db.insert(users).values({ email: 'test@example.com', 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
}
Template: See
templates/transactions.ts

步骤验证内容
1
migrations
目录是否已纳入版本控制(Git)?
2应用到生产环境(
--remote
)前是否已通过
--local
测试?
3敏感数据是否通过
.env
读取?
typescript
import { drizzle } from 'drizzle-orm/d1';
import { users, posts } from './db/schema';

const db = drizzle(env.DB);

// 批量插入
const results = await db.batch([
  db.insert(users).values({ email: 'user1@example.com', name: 'User 1' }),
  db.insert(users).values({ email: 'user2@example.com', name: 'User 2' }),
  db.insert(users).values({ email: 'user3@example.com', name: 'User 3' }),
]);

// 带错误处理的批量操作
try {
  const results = await db.batch([
    db.insert(users).values({ email: 'test@example.com', name: 'Test' }),
    db.insert(posts).values({ title: 'Post', content: 'Content', authorId: 1 }),
  ]);
  console.log('所有操作成功');
} catch (error) {
  console.error('批量操作失败:', error);
  // 必要时进行手动清理
}
模板: 参见
templates/transactions.ts

Pattern 4: Prepared Statements

模式4:预准备语句

typescript
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 });
Note: D1 doesn't cache prepared statements between requests like traditional SQLite.
Template: See
templates/prepared-statements.ts

typescript
import { drizzle } from 'drizzle-orm/d1';
import { users } from './db/schema';
import { eq } from 'drizzle-orm';

const db = drizzle(env.DB);

// 预准备语句(可复用查询)
const getUserById = db
  .select()
  .from(users)
  .where(eq(users.id, sql.placeholder('id')))
  .prepare();

// 使用不同参数执行
const user1 = await getUserById.get({ id: 1 });
const user2 = await getUserById.get({ id: 2 });
注意: D1不会像传统SQLite那样在请求之间缓存预准备语句。
模板: 参见
templates/prepared-statements.ts

Using Bundled Resources

使用捆绑资源

Scripts (scripts/)

脚本(scripts/)

check-versions.sh - Verify package versions are up to date
bash
./scripts/check-versions.sh
Output:
Checking Drizzle ORM versions...
✓ drizzle-orm: 0.44.7 (latest)
✓ drizzle-kit: 0.31.5 (latest)

check-versions.sh - 验证包版本是否为最新
bash
./scripts/check-versions.sh
输出示例:
正在检查Drizzle ORM版本...
✓ drizzle-orm: 0.44.7(最新版本)
✓ drizzle-kit: 0.31.5(最新版本)

References (references/)

参考文档(references/)

Claude should load these when you need specific deep-dive information:
  • wrangler-setup.md - Complete Wrangler configuration guide (local vs remote, env vars)
  • schema-patterns.md - All D1/SQLite column types, constraints, indexes
  • migration-workflow.md - Complete migration workflow (generate, test, apply)
  • query-builder-api.md - Full Drizzle query builder API reference
  • common-errors.md - All 12 errors with detailed solutions
  • links-to-official-docs.md - Organized links to official documentation
When to load:
  • User asks about specific column types → load schema-patterns.md
  • User encounters migration errors → load migration-workflow.md + common-errors.md
  • User needs complete API reference → load query-builder-api.md

当你需要特定深度的信息时,Claude会加载这些文档:
  • wrangler-setup.md - 完整的Wrangler配置指南(本地vs远程,环境变量)
  • 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

Advanced Topics

高级主题

TypeScript Type Inference

TypeScript类型推断

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: 'test@example.com',
  name: 'Test User',
  // createdAt is optional (has default)
};

typescript
import { InferSelectModel, InferInsertModel } from 'drizzle-orm';
import { users } from './db/schema';

// 从模式推断类型
export type User = InferSelectModel<typeof users>;
export type NewUser = InferInsertModel<typeof users>;

// 使用示例
const user: User = await db.select().from(users).where(eq(users.id, 1)).get();

const newUser: NewUser = {
  email: 'test@example.com',
  name: 'Test User',
  // createdAt为可选(已设置默认值)
};

Migration Workflow Best Practices

迁移工作流最佳实践

Development:
  1. Make schema changes in
    src/db/schema.ts
  2. Generate migration:
    npm run db:generate
  3. Review generated SQL in
    ./migrations
  4. Apply locally:
    npm run db:migrate:local
  5. Test in local dev:
    npm run dev
  6. Commit migration files to Git
Production:
  1. Deploy code:
    npm run deploy
  2. Apply migration:
    npm run db:migrate:remote
  3. Verify in production
Reference: See
references/migration-workflow.md

开发阶段:
  1. src/db/schema.ts
    中修改模式
  2. 生成迁移:
    npm run db:generate
  3. 检查
    ./migrations
    目录中生成的SQL
  4. 应用到本地:
    npm run db:migrate:local
  5. 在本地开发环境测试:
    npm run dev
  6. 将迁移文件提交到Git
生产阶段:
  1. 部署代码:
    npm run deploy
  2. 应用迁移:
    npm run db:migrate:remote
  3. 在生产环境验证
参考: 参见
references/migration-workflow.md

Working with Dates

日期处理

D1/SQLite doesn't have native date type. Use integer with timestamp mode:
typescript
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();

D1/SQLite不支持原生日期类型,使用带时间戳模式的整数类型:
typescript
export const events = sqliteTable('events', {
  id: integer('id').primaryKey({ autoIncrement: true }),

  // ✅ 使用带时间戳模式的整数类型
  createdAt: integer('created_at', { mode: 'timestamp' }).$defaultFn(() => new Date()),

  // ❌ 不要使用文本类型存储日期
  // createdAt: text('created_at'),
});

// 使用日期比较查询
const recentEvents = await db
  .select()
  .from(events)
  .where(gt(events.createdAt, new Date('2024-01-01')))
  .all();

Dependencies

依赖项

Required:
  • drizzle-orm@0.44.7
    - ORM runtime
  • drizzle-kit@0.31.5
    - CLI tool for migrations
Optional:
  • better-sqlite3@12.4.1
    - For local SQLite development
  • @cloudflare/workers-types@4.20251014.0
    - TypeScript types
Skills:
  • cloudflare-d1 - D1 database creation and raw SQL queries
  • cloudflare-worker-base - Worker project structure and Hono setup

必需:
  • drizzle-orm@0.44.7
    - ORM运行时
  • drizzle-kit@0.31.5
    - 用于迁移的CLI工具
可选:
  • better-sqlite3@12.4.1
    - 用于本地SQLite开发
  • @cloudflare/workers-types@4.20251014.0
    - TypeScript类型定义
相关技能:
  • cloudflare-d1 - D1数据库创建和原始SQL查询
  • cloudflare-worker-base - Worker项目结构和Hono设置

Official Documentation

官方文档

Package Versions (Verified 2025-10-24)

包版本(2025-10-24已验证)

json
{
  "dependencies": {
    "drizzle-orm": "^0.44.7"
  },
  "devDependencies": {
    "drizzle-kit": "^0.31.5",
    "@cloudflare/workers-types": "^4.20251014.0",
    "better-sqlite3": "^12.4.1"
  }
}

json
{
  "dependencies": {
    "drizzle-orm": "^0.44.7"
  },
  "devDependencies": {
    "drizzle-kit": "^0.31.5",
    "@cloudflare/workers-types": "^4.20251014.0",
    "better-sqlite3": "^12.4.1"
  }
}

Production Example

生产示例

This skill is based on production patterns from:
  • Cloudflare Workers + D1: Serverless edge databases
  • Drizzle ORM: Type-safe ORM used in production apps
  • Errors: 0 (all 12 known issues prevented)
  • Validation: ✅ Complete blog example (users, posts, comments)

本指南基于以下生产环境模式构建:
  • Cloudflare Workers + D1: 无服务器边缘数据库
  • Drizzle ORM: 生产环境中使用的类型安全ORM
  • 错误: 0(所有12个已知问题均已预防)
  • 验证: ✅ 完整博客示例(用户、文章、评论)

Troubleshooting

故障排除

Problem:
D1_ERROR: Cannot use BEGIN TRANSACTION

问题:
D1_ERROR: Cannot use BEGIN TRANSACTION

Solution: Use
db.batch()
instead of
db.transaction()
(see Known Issue #1)
解决方案: 使用
db.batch()
替代
db.transaction()
(参见已知问题#1)

Problem: Foreign key constraint failed during migration

问题: 迁移过程中外键约束失败

Solution: Define cascading deletes and ensure proper migration order (see Known Issue #2)
解决方案: 定义级联删除并确保迁移顺序正确(参见已知问题#2)

Problem: Migration not applying

问题: 迁移未应用

Solution: Test locally first with
--local
flag, review generated SQL (see Known Issue #5)
解决方案: 先使用
--local
标志在本地测试,检查生成的SQL(参见已知问题#5)

Problem: TypeScript type errors with relations

问题: 关联导致TypeScript类型错误

Solution: Use explicit type annotations with
InferSelectModel
(see Known Issue #6)
解决方案: 使用
InferSelectModel
添加显式类型注解(参见已知问题#6)

Problem: env.DB is undefined

问题: env.DB未定义

Solution: Check wrangler.jsonc binding names match code (see Known Issue #4)

解决方案: 检查wrangler.jsonc中的绑定名称是否与代码匹配(参见已知问题#4)

Complete Setup Checklist

完整设置检查清单

  • Installed drizzle-orm and drizzle-kit
  • Created drizzle.config.ts in project root
  • Set up environment variables (CLOUDFLARE_ACCOUNT_ID, etc.)
  • Updated wrangler.jsonc with D1 bindings and migrations_dir
  • Defined schema in src/db/schema.ts
  • Generated first migration with
    drizzle-kit generate
  • Applied migration locally with
    wrangler d1 migrations apply --local
  • Tested queries in Worker
  • Applied migration to production with
    --remote
  • Deployed Worker with
    wrangler deploy
  • Verified all package versions are correct
  • Set up npm scripts for common tasks

Questions? Issues?
  1. Check
    references/common-errors.md
    for all 12 known issues
  2. Verify all steps in the setup process
  3. Check official docs: https://orm.drizzle.team/docs/connect-cloudflare-d1
  4. Ensure D1 database is created and binding is configured

Token Savings: ~60% compared to manual setup Error Prevention: 100% (all 12 known issues documented and prevented) Ready for production!
Drizzle ORM v1.1 - Enhanced
  • 已安装drizzle-orm和drizzle-kit
  • 已在项目根目录创建drizzle.config.ts
  • 已设置环境变量(CLOUDFLARE_ACCOUNT_ID等)
  • 已更新wrangler.jsonc,添加D1绑定和migrations_dir
  • 已在src/db/schema.ts中定义模式
  • 已使用
    drizzle-kit generate
    生成首次迁移
  • 已使用
    wrangler d1 migrations apply --local
    将迁移应用到本地数据库
  • 已在Worker中测试查询
  • 已使用
    --remote
    将迁移应用到生产环境
  • 已使用
    wrangler deploy
    部署Worker
  • 已验证所有包版本正确
  • 已设置npm脚本用于常见任务

有疑问?遇到问题?
  1. 查看
    references/common-errors.md
    获取所有12个已知问题的解决方案
  2. 验证设置流程中的所有步骤
  3. 查看官方文档: https://orm.drizzle.team/docs/connect-cloudflare-d1
  4. 确保D1数据库已创建且绑定已配置

Token节省: 相比手动设置节省约60% 错误预防: 100%(所有12个已知问题均已记录并预防) 已就绪可用于生产环境!
Drizzle ORM v1.1 - 增强版

🔄 Workflow

🔄 工作流

Aşama 1: Schema Definition

步骤1:模式定义

  • Types:
    text
    ,
    integer
    gibi doğru veri tiplerini seç (SQLite/D1 uyumlu).
  • Constraints:
    notNull()
    ,
    unique()
    ,
    primaryKey()
    kısıtlarını tanımla.
  • Relations: Tablolar arası ilişkileri
    relations()
    fonksiyonu ile belirt.
  • 类型: 选择
    text
    integer
    等兼容SQLite/D1的正确数据类型。
  • 约束: 定义
    notNull()
    unique()
    primaryKey()
    等约束。
  • 关联: 使用
    relations()
    函数定义表之间的关联。

Aşama 2: Migration Lifecycle

步骤2:迁移生命周期

  • Generate:
    drizzle-kit generate
    ile SQL oluştur.
  • Review: Oluşan
    .sql
    dosyasını manuel kontrol et (Veri kaybı riski?).
  • Apply:
    wrangler d1 migrations apply --local
    ile önce lokalde test et.
  • 生成: 使用
    drizzle-kit generate
    生成SQL。
  • 检查: 手动检查生成的
    .sql
    文件(是否有数据丢失风险?)。
  • 应用: 先使用
    wrangler d1 migrations apply --local
    在本地测试。

Aşama 3: Query Implementation

步骤3:查询实现

  • Queries:
    .select().from()
    ile type-safe sorgular yaz.
  • Performance: N+1 sorununu
    include
    veya
    with
    kullanarak önle.
  • Batch: Toplu işlemleri
    db.batch([])
    içine al.
  • 查询: 使用
    .select().from()
    编写类型安全查询。
  • 性能: 使用
    include
    with
    避免N+1问题。
  • 批量: 将批量操作放入
    db.batch([])
    中执行。

Kontrol Noktaları

检查点

AşamaDoğrulama
1
migrations
klasörü versiyon kontrolünde (Git) mi?
2Üretim ortamına (
--remote
) geçmeden önce
--local
test edildi mi?
3Hassas veriler
.env
üzerinden mi okunuyor?
步骤验证内容
1
migrations
目录是否已纳入版本控制(Git)?
2应用到生产环境(
--remote
)前是否已通过
--local
测试?
3敏感数据是否通过
.env
读取?