@ 细雨适应器/ d1
Cloudflare D1适配器实施 Drizzle适配器生态系统.
概览
这个 @drizzle-adapter/d1 软件包为 Drizzle 适应器接口提供 Cloudflare D1 执行。 虽然你不直接与此软件包互动( 您使用 @drizzle-adapter/core 相反,它使得D1在Drizzle适应器生态系统中得到了支持.
为什么是D1?
D1为Cloudflare工人提供了独特的优势:
- 边缘 SQLite: 为边缘所建的分布式 SQLite 数据库
- 0 配置: 自动复制和分发
- 全球业绩: 数据来自最接近的边缘位置
- 云浮工人融合:与工人的土著融合
- 自动缩放: 内置可伸缩性和可靠性
- 成本效益: 以慷慨的自由等级纳入工人定价
- SQLite 兼容性: 熟悉的 SQLite 接口和特性
- 自动备份: 内置备份并恢复能力
完美为:
- 边缘应用程序: 数据来自最近的地点
- 云花岗工人:工人应用程序的土著数据库
- 全球应用自动全局分布
- 成本敏感项目: 列入工人定价
- 简单部署: 需要零配置
安装
# Install both the core package and the D1 adapter
pnpm install @drizzle-adapter/core @drizzle-adapter/d1
重要:适配器注册
适配器要正确使用 DrizzleAdapterFactory 操作,必须导入它进行自我注册的副作用:
// Import for side effects - adapter will self-register
import '@drizzle-adapter/d1';
// Now you can use the factory
import { DrizzleAdapterFactory } from '@drizzle-adapter/core';
D1 兼容层
这个适配器提供了几个兼容性增强,使D1与现有的Drizzle ORM代码无缝地工作:
执行方法映射
D1的 Drizzle 执行用途 run() 而不是标准 execute() 方法。 此适配器自动映射 execute() 改为 run() 兼容性:
// Both of these work identically
await client.execute(sql`SELECT * FROM users`);
await client.run(sql`SELECT * FROM users`);
交易兼容性
破门而入2.0.0: Cloudflare D1没有交互交易. transaction() 现在抛出 D1TransactionUnsupportedError 而不是在没有原子性的情况下运行回调。 召回从未被引用.
import { D1TransactionUnsupportedError } from '@drizzle-adapter/d1';
try {
await client.transaction(async (tx) => { /* never runs */ });
} catch (caughtError) {
if (caughtError instanceof D1TransactionUnsupportedError) {
// use db.batch() instead
}
}
用于原子操作,使用时 db.batch()。它是一个隐含的 SQL 交易(全部或无) :
import { sql } from 'drizzle-orm';
const results = await client.batch([
client.insert(users).values({ name: 'John' }),
client.run(sql`UPDATE posts SET published = 1 WHERE id = ${postId}`)
]);
移徙因素
- 交易兼容性: 自2.0起,
db.transaction()掷出D1TransactionUnsupportedError; 迁移到db.batch() - 无回滚: 失败的操作无法自动回滚
- 批量操作: 使用
db.batch()用于需要原子化的操作 - 处理错误: 对关键业务实施人工清理逻辑
释放
pnpm check && pnpm lint && pnpm test
pnpm --filter @drizzle-adapter/d1 publish --access public # run from the repo root
使用量
云花岗工人设置
首先,设置您的 D1 数据库:
# Create a new D1 database
wrangler d1 create my-database
# Add to wrangler.toml
[[d1_databases]]
binding = "DB"
database_name = "my-database"
database_id = "your-database-id"
配置
import { DrizzleAdapterFactory, TypeDrizzleDatabaseConfig } from '@drizzle-adapter/core';
import { D1Database } from '@cloudflare/workers-types';
interface Env {
DB: D1Database;
}
const config: TypeDrizzleDatabaseConfig = {
DATABASE_DRIVER: 'd1',
DATABASE: env.DB
};
const factory = new DrizzleAdapterFactory();
const adapter = factory.create(config);
关于连接的重要注释
虽然D1在工人环境内运行,不需要传统的数据库连接,但这个适配器执行兼容性的标准连接接口. 这意味着您的代码在不同的数据库适配器中仍可移植:
// These connection operations are no-ops for D1 but ensure
// your code remains portable if you switch to another database
const connection = await adapter.getConnection();
const client = connection.getClient();
// Your database operations
await connection.disconnect(); // No-op for D1
Schema 定义
const dataTypes = adapter.getDataTypes();
const users = dataTypes.dbTable('users', {
id: dataTypes.dbInteger('id').primaryKey().autoincrement(),
name: dataTypes.dbText('name').notNull(),
email: dataTypes.dbText('email').notNull().unique(),
metadata: dataTypes.dbText('metadata'), // JSON stored as text
createdAt: dataTypes.dbInteger('created_at')
.default(sql`(unixepoch())`)
});
const posts = dataTypes.dbTable('posts', {
id: dataTypes.dbInteger('id').primaryKey().autoincrement(),
userId: dataTypes.dbInteger('user_id')
.references(() => users.id),
title: dataTypes.dbText('title').notNull(),
content: dataTypes.dbText('content').notNull(),
published: dataTypes.dbInteger('published').default(0),
createdAt: dataTypes.dbInteger('created_at')
.default(sql`(unixepoch())`)
});
基本 CRUD 操作
import { eq, and, or, desc, sql } from 'drizzle-orm';
const client = await adapter.getConnection().getClient();
// INSERT
// Single insert with returning
const [newUser] = await client
.insert(users)
.values({
name: 'John Doe',
email: '[email protected]',
metadata: JSON.stringify({ role: 'user' })
})
.returning();
// Bulk insert
await client
.insert(posts)
.values([
{
userId: newUser.id,
title: 'First Post',
content: 'Hello, world!'
},
{
userId: newUser.id,
title: 'Second Post',
content: 'Another post'
}
]);
// SELECT
// Select all
const allUsers = await client
.select()
.from(users);
// Select with conditions
const user = await client
.select()
.from(users)
.where(eq(users.email, '[email protected]'));
// Select with join
const userPosts = await client
.select({
userName: users.name,
postTitle: posts.title,
content: posts.content,
metadata: users.metadata
})
.from(posts)
.leftJoin(users, eq(posts.userId, users.id))
.where(eq(posts.published, 1))
.orderBy(desc(posts.createdAt));
// UPDATE
await client
.update(users)
.set({
name: 'John Smith',
metadata: JSON.stringify({ role: 'admin' })
})
.where(eq(users.id, newUser.id));
// DELETE
await client
.delete(posts)
.where(
and(
eq(posts.userId, newUser.id),
eq(posts.published, 0)
)
);
工人执行
interface Env {
DB: D1Database;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const factory = new DrizzleAdapterFactory();
const adapter = factory.create({
DATABASE_DRIVER: 'd1',
DATABASE: env.DB
});
const client = await adapter.getConnection().getClient();
const url = new URL(request.url);
try {
switch (url.pathname) {
case '/users': {
const users = await client
.select()
.from(users);
return Response.json(users);
}
case '/posts': {
const posts = await client
.select({
title: posts.title,
content: posts.content,
author: users.name
})
.from(posts)
.leftJoin(users, eq(posts.userId, users.id))
.where(eq(posts.published, 1))
.orderBy(desc(posts.createdAt));
return Response.json(posts);
}
default:
return new Response('Not Found', { status: 404 });
}
} catch (error) {
return new Response('Error', { status: 500 });
}
}
}
最佳做法
- 边缘优化: 查询会自动优化边缘执行
- JSON 处理: 将 JSON 数据作为 TEXT 存储,并根据需要进行解析/字符串
- 处理错误: 为边缘环境执行适当的错误处理
- 查询优化: 尽量减少每个请求的查询次数
- 可移动性: 将连接接口用于数据库可移植性
捐款
我们欢迎捐款! 请随意提交拉力请求 对于重大变动,请先开出一个问题来讨论你想要改变什么.
相关软件包
- @ 细雨适应器/ 核心 - 核心接口和类型
- @ 细雨适应器/ sqlite- core - 共享 SQLite 功能