cloudflare-d1

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Cloudflare D1 Skill

Cloudflare D1 使用指南

Load with: base.md + typescript.md
Cloudflare D1 is a serverless SQLite database designed for Cloudflare Workers with global distribution and zero cold starts.

加载依赖:base.md + typescript.md
Cloudflare D1 是一款为Cloudflare Workers打造的无服务器SQLite数据库,具备全球分布式部署和零冷启动特性。

Core Principle

核心原则

SQLite at the edge, migrations in version control, Drizzle for type safety.
D1 brings SQLite's simplicity to serverless. Design for horizontal scale (multiple small databases) rather than vertical (one large database). Use Drizzle ORM for type-safe queries and migrations.

边缘环境运行SQLite,迁移纳入版本控制,使用Drizzle保障类型安全。
D1 将SQLite的简洁性带入无服务器场景。设计时应优先考虑水平扩展(多个小型数据库)而非垂直扩展(单个大型数据库)。使用Drizzle ORM实现类型安全的查询与迁移。

D1 Stack

D1 技术栈

ComponentPurpose
D1Serverless SQLite database
WorkersEdge runtime for your application
WranglerCLI for development and deployment
Drizzle ORMType-safe ORM with migrations
Drizzle KitMigration tooling
HonoLightweight web framework (optional)

组件用途
D1无服务器SQLite数据库
Workers应用的边缘运行时
Wrangler开发与部署用CLI工具
Drizzle ORM带迁移功能的类型安全ORM
Drizzle Kit迁移工具集
Hono轻量级Web框架(可选)

Project Setup

项目搭建

Create Worker Project

创建Worker项目

bash
undefined
bash
undefined

Create new project

创建新项目

npm create cloudflare@latest my-app -- --template "worker-typescript" cd my-app
npm create cloudflare@latest my-app -- --template "worker-typescript" cd my-app

Install dependencies

安装依赖

npm install drizzle-orm npm install -D drizzle-kit
undefined
npm install drizzle-orm npm install -D drizzle-kit
undefined

Create D1 Database

创建D1数据库

bash
undefined
bash
undefined

Create database (creates both local and remote)

创建数据库(同时创建本地和远程实例)

npx wrangler d1 create my-database
npx wrangler d1 create my-database

Output:

输出内容:

[[d1_databases]]

[[d1_databases]]

binding = "DB"

binding = "DB"

database_name = "my-database"

database_name = "my-database"

database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"

database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"

undefined
undefined

Configure wrangler.toml

配置wrangler.toml

toml
name = "my-app"
main = "src/index.ts"
compatibility_date = "2024-01-01"

[[d1_databases]]
binding = "DB"
database_name = "my-database"
database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
migrations_dir = "drizzle"
migrations_table = "drizzle_migrations"
toml
name = "my-app"
main = "src/index.ts"
compatibility_date = "2024-01-01"

[[d1_databases]]
binding = "DB"
database_name = "my-database"
database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
migrations_dir = "drizzle"
migrations_table = "drizzle_migrations"

Generate TypeScript Types

生成TypeScript类型

bash
undefined
bash
undefined

Generate env types from wrangler.toml

从wrangler.toml生成环境变量类型

npx wrangler types
npx wrangler types

Creates worker-configuration.d.ts:

创建worker-configuration.d.ts文件:

interface Env {

interface Env {

DB: D1Database;

DB: D1Database;

}

}


---

---

Drizzle ORM Setup

Drizzle ORM 配置

Schema Definition

模式定义

typescript
// src/db/schema.ts
import { sqliteTable, text, integer, real, blob } from 'drizzle-orm/sqlite-core';
import { sql } from 'drizzle-orm';

export const users = sqliteTable('users', {
  id: integer('id').primaryKey({ autoIncrement: true }),
  email: text('email').notNull().unique(),
  name: text('name').notNull(),
  role: text('role', { enum: ['user', 'admin'] }).default('user'),
  createdAt: text('created_at').default(sql`CURRENT_TIMESTAMP`),
  updatedAt: text('updated_at').default(sql`CURRENT_TIMESTAMP`)
});

export const posts = sqliteTable('posts', {
  id: integer('id').primaryKey({ autoIncrement: true }),
  title: text('title').notNull(),
  content: text('content'),
  authorId: integer('author_id').references(() => users.id),
  published: integer('published', { mode: 'boolean' }).default(false),
  viewCount: integer('view_count').default(0),
  createdAt: text('created_at').default(sql`CURRENT_TIMESTAMP`)
});

export const tags = sqliteTable('tags', {
  id: integer('id').primaryKey({ autoIncrement: true }),
  name: text('name').notNull().unique()
});

export const postTags = sqliteTable('post_tags', {
  postId: integer('post_id').references(() => posts.id),
  tagId: integer('tag_id').references(() => tags.id)
});

// Type exports
export type User = typeof users.$inferSelect;
export type NewUser = typeof users.$inferInsert;
export type Post = typeof posts.$inferSelect;
export type NewPost = typeof posts.$inferInsert;
typescript
// src/db/schema.ts
import { sqliteTable, text, integer, real, blob } from 'drizzle-orm/sqlite-core';
import { sql } from 'drizzle-orm';

export const users = sqliteTable('users', {
  id: integer('id').primaryKey({ autoIncrement: true }),
  email: text('email').notNull().unique(),
  name: text('name').notNull(),
  role: text('role', { enum: ['user', 'admin'] }).default('user'),
  createdAt: text('created_at').default(sql`CURRENT_TIMESTAMP`),
  updatedAt: text('updated_at').default(sql`CURRENT_TIMESTAMP`)
});

export const posts = sqliteTable('posts', {
  id: integer('id').primaryKey({ autoIncrement: true }),
  title: text('title').notNull(),
  content: text('content'),
  authorId: integer('author_id').references(() => users.id),
  published: integer('published', { mode: 'boolean' }).default(false),
  viewCount: integer('view_count').default(0),
  createdAt: text('created_at').default(sql`CURRENT_TIMESTAMP`)
});

export const tags = sqliteTable('tags', {
  id: integer('id').primaryKey({ autoIncrement: true }),
  name: text('name').notNull().unique()
});

export const postTags = sqliteTable('post_tags', {
  postId: integer('post_id').references(() => posts.id),
  tagId: integer('tag_id').references(() => tags.id)
});

// 类型导出
export type User = typeof users.$inferSelect;
export type NewUser = typeof users.$inferInsert;
export type Post = typeof posts.$inferSelect;
export type NewPost = typeof posts.$inferInsert;

Drizzle Config

Drizzle 配置文件

typescript
// drizzle.config.ts
import { defineConfig } from 'drizzle-kit';

export default defineConfig({
  schema: './src/db/schema.ts',
  out: './drizzle',
  dialect: 'sqlite',
  driver: 'd1-http',
  dbCredentials: {
    accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
    databaseId: process.env.CLOUDFLARE_DATABASE_ID!,
    token: process.env.CLOUDFLARE_D1_TOKEN!
  }
});
typescript
// drizzle.config.ts
import { defineConfig } from 'drizzle-kit';

export default defineConfig({
  schema: './src/db/schema.ts',
  out: './drizzle',
  dialect: 'sqlite',
  driver: 'd1-http',
  dbCredentials: {
    accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
    databaseId: process.env.CLOUDFLARE_DATABASE_ID!,
    token: process.env.CLOUDFLARE_D1_TOKEN!
  }
});

Database Client

数据库客户端

typescript
// src/db/index.ts
import { drizzle } from 'drizzle-orm/d1';
import * as schema from './schema';

export function createDb(d1: D1Database) {
  return drizzle(d1, { schema });
}

export type Database = ReturnType<typeof createDb>;
export * from './schema';

typescript
// src/db/index.ts
import { drizzle } from 'drizzle-orm/d1';
import * as schema from './schema';

export function createDb(d1: D1Database) {
  return drizzle(d1, { schema });
}

export type Database = ReturnType<typeof createDb>;
export * from './schema';

Migration Workflow

迁移工作流

Generate Migration

生成迁移文件

bash
undefined
bash
undefined

Generate migration from schema changes

根据模式变更生成迁移文件

npx drizzle-kit generate
npx drizzle-kit generate

Output: drizzle/0000_initial.sql

输出:drizzle/0000_initial.sql

undefined
undefined

Apply Migrations Locally

本地应用迁移

bash
undefined
bash
undefined

Apply to local D1

应用到本地D1数据库

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

Or via Drizzle

或通过Drizzle执行

npx drizzle-kit migrate
undefined
npx drizzle-kit migrate
undefined

Apply Migrations to Production

生产环境应用迁移

bash
undefined
bash
undefined

Apply to remote D1

应用到远程D1数据库

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

Preview first (dry run)

先预览(试运行)

npx wrangler d1 migrations apply my-database --remote --dry-run
undefined
npx wrangler d1 migrations apply my-database --remote --dry-run
undefined

Migration File Example

迁移文件示例

sql
-- drizzle/0000_initial.sql
CREATE TABLE `users` (
  `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
  `email` text NOT NULL,
  `name` text NOT NULL,
  `role` text DEFAULT 'user',
  `created_at` text DEFAULT CURRENT_TIMESTAMP,
  `updated_at` text DEFAULT CURRENT_TIMESTAMP
);

CREATE UNIQUE INDEX `users_email_unique` ON `users` (`email`);

CREATE TABLE `posts` (
  `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
  `title` text NOT NULL,
  `content` text,
  `author_id` integer REFERENCES `users`(`id`),
  `published` integer DEFAULT false,
  `view_count` integer DEFAULT 0,
  `created_at` text DEFAULT CURRENT_TIMESTAMP
);

sql
-- drizzle/0000_initial.sql
CREATE TABLE `users` (
  `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
  `email` text NOT NULL,
  `name` text NOT NULL,
  `role` text DEFAULT 'user',
  `created_at` text DEFAULT CURRENT_TIMESTAMP,
  `updated_at` text DEFAULT CURRENT_TIMESTAMP
);

CREATE UNIQUE INDEX `users_email_unique` ON `users` (`email`);

CREATE TABLE `posts` (
  `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
  `title` text NOT NULL,
  `content` text,
  `author_id` integer REFERENCES `users`(`id`),
  `published` integer DEFAULT false,
  `view_count` integer DEFAULT 0,
  `created_at` text DEFAULT CURRENT_TIMESTAMP
);

Worker Implementation

Worker 实现

Basic Worker with Hono

基于Hono的基础Worker

typescript
// src/index.ts
import { Hono } from 'hono';
import { createDb, users, posts } from './db';
import { eq, desc } from 'drizzle-orm';

type Bindings = {
  DB: D1Database;
};

const app = new Hono<{ Bindings: Bindings }>();

// Middleware to inject db
app.use('*', async (c, next) => {
  c.set('db', createDb(c.env.DB));
  await next();
});

// List users
app.get('/users', async (c) => {
  const db = c.get('db');
  const allUsers = await db.select().from(users);
  return c.json(allUsers);
});

// Get user by ID
app.get('/users/:id', async (c) => {
  const db = c.get('db');
  const id = parseInt(c.req.param('id'));

  const user = await db.select().from(users).where(eq(users.id, id)).get();

  if (!user) {
    return c.json({ error: 'User not found' }, 404);
  }
  return c.json(user);
});

// Create user
app.post('/users', async (c) => {
  const db = c.get('db');
  const body = await c.req.json<{ email: string; name: string }>();

  const result = await db.insert(users).values({
    email: body.email,
    name: body.name
  }).returning();

  return c.json(result[0], 201);
});

// Update user
app.put('/users/:id', async (c) => {
  const db = c.get('db');
  const id = parseInt(c.req.param('id'));
  const body = await c.req.json<Partial<{ email: string; name: string }>>();

  const result = await db.update(users)
    .set({ ...body, updatedAt: new Date().toISOString() })
    .where(eq(users.id, id))
    .returning();

  if (result.length === 0) {
    return c.json({ error: 'User not found' }, 404);
  }
  return c.json(result[0]);
});

// Delete user
app.delete('/users/:id', async (c) => {
  const db = c.get('db');
  const id = parseInt(c.req.param('id'));

  const result = await db.delete(users).where(eq(users.id, id)).returning();

  if (result.length === 0) {
    return c.json({ error: 'User not found' }, 404);
  }
  return c.json({ deleted: true });
});

export default app;
typescript
// src/index.ts
import { Hono } from 'hono';
import { createDb, users, posts } from './db';
import { eq, desc } from 'drizzle-orm';

type Bindings = {
  DB: D1Database;
};

const app = new Hono<{ Bindings: Bindings }>();

// 注入数据库的中间件
app.use('*', async (c, next) => {
  c.set('db', createDb(c.env.DB));
  await next();
});

// 列出所有用户
app.get('/users', async (c) => {
  const db = c.get('db');
  const allUsers = await db.select().from(users);
  return c.json(allUsers);
});

// 根据ID获取用户
app.get('/users/:id', async (c) => {
  const db = c.get('db');
  const id = parseInt(c.req.param('id'));

  const user = await db.select().from(users).where(eq(users.id, id)).get();

  if (!user) {
    return c.json({ error: '用户不存在' }, 404);
  }
  return c.json(user);
});

// 创建用户
app.post('/users', async (c) => {
  const db = c.get('db');
  const body = await c.req.json<{ email: string; name: string }>();

  const result = await db.insert(users).values({
    email: body.email,
    name: body.name
  }).returning();

  return c.json(result[0], 201);
});

// 更新用户
app.put('/users/:id', async (c) => {
  const db = c.get('db');
  const id = parseInt(c.req.param('id'));
  const body = await c.req.json<Partial<{ email: string; name: string }>>();

  const result = await db.update(users)
    .set({ ...body, updatedAt: new Date().toISOString() })
    .where(eq(users.id, id))
    .returning();

  if (result.length === 0) {
    return c.json({ error: '用户不存在' }, 404);
  }
  return c.json(result[0]);
});

// 删除用户
app.delete('/users/:id', async (c) => {
  const db = c.get('db');
  const id = parseInt(c.req.param('id'));

  const result = await db.delete(users).where(eq(users.id, id)).returning();

  if (result.length === 0) {
    return c.json({ error: '用户不存在' }, 404);
  }
  return c.json({ deleted: true });
});

export default app;

Raw D1 API (Without ORM)

原生D1 API(不使用ORM)

typescript
// src/index.ts
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);

    if (url.pathname === '/users' && request.method === 'GET') {
      const { results } = await env.DB.prepare(
        'SELECT * FROM users ORDER BY created_at DESC'
      ).all();
      return Response.json(results);
    }

    if (url.pathname === '/users' && request.method === 'POST') {
      const body = await request.json() as { email: string; name: string };

      const result = await env.DB.prepare(
        'INSERT INTO users (email, name) VALUES (?, ?) RETURNING *'
      ).bind(body.email, body.name).first();

      return Response.json(result, { status: 201 });
    }

    return new Response('Not Found', { status: 404 });
  }
};

typescript
// src/index.ts
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);

    if (url.pathname === '/users' && request.method === 'GET') {
      const { results } = await env.DB.prepare(
        'SELECT * FROM users ORDER BY created_at DESC'
      ).all();
      return Response.json(results);
    }

    if (url.pathname === '/users' && request.method === 'POST') {
      const body = await request.json() as { email: string; name: string };

      const result = await env.DB.prepare(
        'INSERT INTO users (email, name) VALUES (?, ?) RETURNING *'
      ).bind(body.email, body.name).first();

      return Response.json(result, { status: 201 });
    }

    return new Response('未找到资源', { status: 404 });
  }
};

Query Patterns

查询模式

Select Queries

查询语句

typescript
import { eq, and, or, like, gt, desc, asc, count, sql } from 'drizzle-orm';

// Basic select
const allPosts = await db.select().from(posts);

// Select specific columns
const titles = await db.select({ id: posts.id, title: posts.title }).from(posts);

// Where clause
const published = await db.select().from(posts).where(eq(posts.published, true));

// Multiple conditions
const recentPublished = await db.select().from(posts).where(
  and(
    eq(posts.published, true),
    gt(posts.createdAt, '2024-01-01')
  )
);

// OR conditions
const featured = await db.select().from(posts).where(
  or(
    eq(posts.viewCount, 1000),
    like(posts.title, '%featured%')
  )
);

// Order and limit
const topPosts = await db.select()
  .from(posts)
  .orderBy(desc(posts.viewCount))
  .limit(10);

// Pagination
const page2 = await db.select()
  .from(posts)
  .orderBy(desc(posts.createdAt))
  .limit(10)
  .offset(10);

// Count
const postCount = await db.select({ count: count() }).from(posts);
typescript
import { eq, and, or, like, gt, desc, asc, count, sql } from 'drizzle-orm';

// 基础查询
const allPosts = await db.select().from(posts);

// 查询指定列
const titles = await db.select({ id: posts.id, title: posts.title }).from(posts);

// WHERE条件
const published = await db.select().from(posts).where(eq(posts.published, true));

// 多条件查询
const recentPublished = await db.select().from(posts).where(
  and(
    eq(posts.published, true),
    gt(posts.createdAt, '2024-01-01')
  )
);

// OR条件
const featured = await db.select().from(posts).where(
  or(
    eq(posts.viewCount, 1000),
    like(posts.title, '%featured%')
  )
);

// 排序与限制
const topPosts = await db.select()
  .from(posts)
  .orderBy(desc(posts.viewCount))
  .limit(10);

// 分页查询
const page2 = await db.select()
  .from(posts)
  .orderBy(desc(posts.createdAt))
  .limit(10)
  .offset(10);

// 统计数量
const postCount = await db.select({ count: count() }).from(posts);

Joins

关联查询

typescript
// Inner join
const postsWithAuthors = await db.select({
  post: posts,
  author: users
})
.from(posts)
.innerJoin(users, eq(posts.authorId, users.id));

// Left join
const allPostsWithAuthors = await db.select()
  .from(posts)
  .leftJoin(users, eq(posts.authorId, users.id));

// Many-to-many via junction table
const postsWithTags = await db.select({
  post: posts,
  tag: tags
})
.from(posts)
.leftJoin(postTags, eq(posts.id, postTags.postId))
.leftJoin(tags, eq(postTags.tagId, tags.id));
typescript
// 内连接
const postsWithAuthors = await db.select({
  post: posts,
  author: users
})
.from(posts)
.innerJoin(users, eq(posts.authorId, users.id));

// 左连接
const allPostsWithAuthors = await db.select()
  .from(posts)
  .leftJoin(users, eq(posts.authorId, users.id));

// 通过中间表实现多对多关联
const postsWithTags = await db.select({
  post: posts,
  tag: tags
})
.from(posts)
.leftJoin(postTags, eq(posts.id, postTags.postId))
.leftJoin(tags, eq(postTags.tagId, tags.id));

Insert, Update, Delete

插入、更新、删除

typescript
// Insert single
const newUser = await db.insert(users).values({
  email: 'user@example.com',
  name: 'John Doe'
}).returning();

// Insert multiple
await db.insert(users).values([
  { email: 'a@test.com', name: 'Alice' },
  { email: 'b@test.com', name: 'Bob' }
]);

// Upsert (insert or update on conflict)
await db.insert(users)
  .values({ email: 'user@test.com', name: 'New Name' })
  .onConflictDoUpdate({
    target: users.email,
    set: { name: 'New Name' }
  });

// Update
await db.update(posts)
  .set({ published: true })
  .where(eq(posts.id, 1));

// Update with increment
await db.update(posts)
  .set({ viewCount: sql`${posts.viewCount} + 1` })
  .where(eq(posts.id, 1));

// Delete
await db.delete(posts).where(eq(posts.id, 1));
typescript
// 插入单条数据
const newUser = await db.insert(users).values({
  email: 'user@example.com',
  name: 'John Doe'
}).returning();

// 批量插入
await db.insert(users).values([
  { email: 'a@test.com', name: 'Alice' },
  { email: 'b@test.com', name: 'Bob' }
]);

// 插入或更新(冲突时更新)
await db.insert(users)
  .values({ email: 'user@test.com', name: 'New Name' })
  .onConflictDoUpdate({
    target: users.email,
    set: { name: 'New Name' }
  });

// 更新数据
await db.update(posts)
  .set({ published: true })
  .where(eq(posts.id, 1));

// 增量更新
await db.update(posts)
  .set({ viewCount: sql`${posts.viewCount} + 1` })
  .where(eq(posts.id, 1));

// 删除数据
await db.delete(posts).where(eq(posts.id, 1));

Transactions

事务处理

typescript
// D1 supports transactions via batch
const results = await db.batch([
  db.insert(users).values({ email: 'a@test.com', name: 'A' }),
  db.insert(users).values({ email: 'b@test.com', name: 'B' }),
  db.update(posts).set({ published: true }).where(eq(posts.id, 1))
]);

// Raw D1 batch
const batchResults = await env.DB.batch([
  env.DB.prepare('INSERT INTO users (email, name) VALUES (?, ?)').bind('a@test.com', 'A'),
  env.DB.prepare('INSERT INTO users (email, name) VALUES (?, ?)').bind('b@test.com', 'B')
]);

typescript
// D1通过批量操作支持事务
const results = await db.batch([
  db.insert(users).values({ email: 'a@test.com', name: 'A' }),
  db.insert(users).values({ email: 'b@test.com', name: 'B' }),
  db.update(posts).set({ published: true }).where(eq(posts.id, 1))
]);

// 原生D1批量操作
const batchResults = await env.DB.batch([
  env.DB.prepare('INSERT INTO users (email, name) VALUES (?, ?)').bind('a@test.com', 'A'),
  env.DB.prepare('INSERT INTO users (email, name) VALUES (?, ?)').bind('b@test.com', 'B')
]);

Local Development

本地开发

Start Dev Server

启动开发服务器

bash
undefined
bash
undefined

Local development with D1

带D1的本地开发

npx wrangler dev
npx wrangler dev

With specific port

指定端口

npx wrangler dev --port 8787
undefined
npx wrangler dev --port 8787
undefined

Database Management

数据库管理

bash
undefined
bash
undefined

Execute SQL locally

本地执行SQL语句

npx wrangler d1 execute my-database --local --command "SELECT * FROM users"
npx wrangler d1 execute my-database --local --command "SELECT * FROM users"

Execute SQL file

执行SQL文件

npx wrangler d1 execute my-database --local --file ./seed.sql
npx wrangler d1 execute my-database --local --file ./seed.sql

Open SQLite shell

打开SQLite shell

npx wrangler d1 execute my-database --local --command ".tables"
undefined
npx wrangler d1 execute my-database --local --command ".tables"
undefined

Drizzle Studio

Drizzle Studio

bash
undefined
bash
undefined

Run Drizzle Studio for visual DB management

启动Drizzle Studio进行可视化数据库管理

npx drizzle-kit studio
undefined
npx drizzle-kit studio
undefined

Seed Data

数据初始化

sql
-- seed.sql
INSERT INTO users (email, name, role) VALUES
  ('admin@example.com', 'Admin User', 'admin'),
  ('user@example.com', 'Test User', 'user');

INSERT INTO posts (title, content, author_id, published) VALUES
  ('First Post', 'Hello World!', 1, true),
  ('Draft Post', 'Work in progress...', 1, false);
bash
undefined
sql
-- seed.sql
INSERT INTO users (email, name, role) VALUES
  ('admin@example.com', '管理员用户', 'admin'),
  ('user@example.com', '测试用户', 'user');

INSERT INTO posts (title, content, author_id, published) VALUES
  ('第一篇文章', 'Hello World!', 1, true),
  ('草稿文章', '正在撰写中...', 1, false);
bash
undefined

Seed local database

为本地数据库初始化数据

npx wrangler d1 execute my-database --local --file ./seed.sql

---
npx wrangler d1 execute my-database --local --file ./seed.sql

---

Multi-Environment Setup

多环境配置

wrangler.toml

wrangler.toml

toml
name = "my-app"
main = "src/index.ts"
compatibility_date = "2024-01-01"
toml
name = "my-app"
main = "src/index.ts"
compatibility_date = "2024-01-01"

Development

开发环境

[env.dev] [[env.dev.d1_databases]] binding = "DB" database_name = "my-database-dev" database_id = "dev-database-id"
[env.dev] [[env.dev.d1_databases]] binding = "DB" database_name = "my-database-dev" database_id = "dev-database-id"

Staging

预发布环境

[env.staging] [[env.staging.d1_databases]] binding = "DB" database_name = "my-database-staging" database_id = "staging-database-id"
[env.staging] [[env.staging.d1_databases]] binding = "DB" database_name = "my-database-staging" database_id = "staging-database-id"

Production

生产环境

[env.production] [[env.production.d1_databases]] binding = "DB" database_name = "my-database-prod" database_id = "prod-database-id"
undefined
[env.production] [[env.production.d1_databases]] binding = "DB" database_name = "my-database-prod" database_id = "prod-database-id"
undefined

Deploy to Environments

部署到不同环境

bash
undefined
bash
undefined

Deploy to staging

部署到预发布环境

npx wrangler deploy --env staging
npx wrangler deploy --env staging

Deploy to production

部署到生产环境

npx wrangler deploy --env production
npx wrangler deploy --env production

Apply migrations to staging

为预发布环境应用迁移

npx wrangler d1 migrations apply my-database-staging --remote --env staging

---
npx wrangler d1 migrations apply my-database-staging --remote --env staging

---

Testing

测试

Integration Tests

集成测试

typescript
// tests/api.test.ts
import { unstable_dev } from 'wrangler';
import type { UnstableDevWorker } from 'wrangler';
import { describe, beforeAll, afterAll, it, expect } from 'vitest';

describe('API', () => {
  let worker: UnstableDevWorker;

  beforeAll(async () => {
    worker = await unstable_dev('src/index.ts', {
      experimental: { disableExperimentalWarning: true }
    });
  });

  afterAll(async () => {
    await worker.stop();
  });

  it('should list users', async () => {
    const res = await worker.fetch('/users');
    expect(res.status).toBe(200);
    const data = await res.json();
    expect(Array.isArray(data)).toBe(true);
  });

  it('should create user', async () => {
    const res = await worker.fetch('/users', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email: 'test@test.com', name: 'Test' })
    });
    expect(res.status).toBe(201);
  });
});

typescript
// tests/api.test.ts
import { unstable_dev } from 'wrangler';
import type { UnstableDevWorker } from 'wrangler';
import { describe, beforeAll, afterAll, it, expect } from 'vitest';

describe('API', () => {
  let worker: UnstableDevWorker;

  beforeAll(async () => {
    worker = await unstable_dev('src/index.ts', {
      experimental: { disableExperimentalWarning: true }
    });
  });

  afterAll(async () => {
    await worker.stop();
  });

  it('应该列出所有用户', async () => {
    const res = await worker.fetch('/users');
    expect(res.status).toBe(200);
    const data = await res.json();
    expect(Array.isArray(data)).toBe(true);
  });

  it('应该创建用户', async () => {
    const res = await worker.fetch('/users', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email: 'test@test.com', name: '测试用户' })
    });
    expect(res.status).toBe(201);
  });
});

CLI Quick Reference

CLI 速查手册

bash
undefined
bash
undefined

Database

数据库操作

wrangler d1 create <name> # Create database wrangler d1 list # List databases wrangler d1 info <name> # Database info wrangler d1 delete <name> # Delete database
wrangler d1 create <name> # 创建数据库 wrangler d1 list # 列出所有数据库 wrangler d1 info <name> # 查看数据库信息 wrangler d1 delete <name> # 删除数据库

Migrations

迁移操作

wrangler d1 migrations list <name> # List migrations wrangler d1 migrations apply <name> --local # Apply locally wrangler d1 migrations apply <name> --remote # Apply to production
wrangler d1 migrations list <name> # 列出所有迁移 wrangler d1 migrations apply <name> --local # 本地应用迁移 wrangler d1 migrations apply <name> --remote # 生产环境应用迁移

SQL execution

SQL执行

wrangler d1 execute <name> --command "SQL" # Run SQL wrangler d1 execute <name> --file ./file.sql # Run SQL file wrangler d1 execute <name> --local # Run on local wrangler d1 execute <name> --remote # Run on production
wrangler d1 execute <name> --command "SQL" # 执行SQL语句 wrangler d1 execute <name> --file ./file.sql # 执行SQL文件 wrangler d1 execute <name> --local # 在本地执行 wrangler d1 execute <name> --remote # 在生产环境执行

Development

开发操作

wrangler dev # Start local server wrangler types # Generate TypeScript types wrangler deploy # Deploy to production
wrangler dev # 启动本地服务器 wrangler types # 生成TypeScript类型 wrangler deploy # 部署到生产环境

Drizzle

Drizzle操作

drizzle-kit generate # Generate migrations drizzle-kit migrate # Apply migrations drizzle-kit studio # Open Drizzle Studio drizzle-kit push # Push schema (dev only)

---
drizzle-kit generate # 生成迁移文件 drizzle-kit migrate # 应用迁移 drizzle-kit studio # 打开Drizzle Studio drizzle-kit push # 推送模式(仅开发环境)

---

D1 Limits & Considerations

D1 限制与注意事项

LimitValue
Database size10 GB max
Row size1 MB max
SQL statement100 KB max
Batch size1000 statements
Reads per day (free)5 million
Writes per day (free)100,000

限制项数值
数据库最大容量10 GB
单条记录最大大小1 MB
SQL语句最大长度100 KB
批量操作最大语句数1000条
免费版每日读取次数500万次
免费版每日写入次数10万次

Anti-Patterns

反模式

  • Single large database - Design for multiple smaller databases (per-tenant)
  • No migrations - Always version control schema changes
  • Raw SQL everywhere - Use Drizzle for type safety
  • No connection to remote - Always test against real D1 before deploy
  • Large blobs in D1 - Use R2 for file storage
  • Complex joins - D1 is SQLite; keep queries simple
  • No batching - Use batch for multiple operations
  • Ignoring limits - Monitor usage on free tier
  • 单个大型数据库 - 应设计为多个小型数据库(按租户划分)
  • 不使用迁移 - 务必将模式变更纳入版本控制
  • 全程使用原生SQL - 使用Drizzle保障类型安全
  • 不连接远程数据库测试 - 部署前务必在真实D1环境测试
  • 在D1中存储大文件 - 使用R2存储文件
  • 复杂关联查询 - D1基于SQLite,应保持查询简洁
  • 不使用批量操作 - 多操作时使用批量处理提升效率
  • 忽略限制条件 - 免费版需监控使用量