cloudflare-d1
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCloudflare 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 技术栈
| Component | Purpose |
|---|---|
| D1 | Serverless SQLite database |
| Workers | Edge runtime for your application |
| Wrangler | CLI for development and deployment |
| Drizzle ORM | Type-safe ORM with migrations |
| Drizzle Kit | Migration tooling |
| Hono | Lightweight web framework (optional) |
| 组件 | 用途 |
|---|---|
| D1 | 无服务器SQLite数据库 |
| Workers | 应用的边缘运行时 |
| Wrangler | 开发与部署用CLI工具 |
| Drizzle ORM | 带迁移功能的类型安全ORM |
| Drizzle Kit | 迁移工具集 |
| Hono | 轻量级Web框架(可选) |
Project Setup
项目搭建
Create Worker Project
创建Worker项目
bash
undefinedbash
undefinedCreate 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
undefinednpm install drizzle-orm
npm install -D drizzle-kit
undefinedCreate D1 Database
创建D1数据库
bash
undefinedbash
undefinedCreate 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"
undefinedundefinedConfigure 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
undefinedbash
undefinedGenerate 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
undefinedbash
undefinedGenerate migration from schema changes
根据模式变更生成迁移文件
npx drizzle-kit generate
npx drizzle-kit generate
Output: drizzle/0000_initial.sql
输出:drizzle/0000_initial.sql
undefinedundefinedApply Migrations Locally
本地应用迁移
bash
undefinedbash
undefinedApply 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
undefinednpx drizzle-kit migrate
undefinedApply Migrations to Production
生产环境应用迁移
bash
undefinedbash
undefinedApply 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
undefinednpx wrangler d1 migrations apply my-database --remote --dry-run
undefinedMigration 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
undefinedbash
undefinedLocal development with D1
带D1的本地开发
npx wrangler dev
npx wrangler dev
With specific port
指定端口
npx wrangler dev --port 8787
undefinednpx wrangler dev --port 8787
undefinedDatabase Management
数据库管理
bash
undefinedbash
undefinedExecute 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"
undefinednpx wrangler d1 execute my-database --local --command ".tables"
undefinedDrizzle Studio
Drizzle Studio
bash
undefinedbash
undefinedRun Drizzle Studio for visual DB management
启动Drizzle Studio进行可视化数据库管理
npx drizzle-kit studio
undefinednpx drizzle-kit studio
undefinedSeed 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
undefinedsql
-- 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
undefinedSeed 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"
undefinedDeploy to Environments
部署到不同环境
bash
undefinedbash
undefinedDeploy 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
undefinedbash
undefinedDatabase
数据库操作
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 限制与注意事项
| Limit | Value |
|---|---|
| Database size | 10 GB max |
| Row size | 1 MB max |
| SQL statement | 100 KB max |
| Batch size | 1000 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,应保持查询简洁
- 不使用批量操作 - 多操作时使用批量处理提升效率
- 忽略限制条件 - 免费版需监控使用量