Drizzle ORM:輕量級數據庫工具
Drizzle ORM:輕量級數據庫工具
在上一章中,我們探討了 Cloudflare D1 如何作為一款高性能、低成本的邊緣數據庫解決方案,徹底改變了我們對數據庫架構的認知.
但一般來說,我們很少在項目里裸寫sql,所以我們需要一個能簡化操作和開發的ORM工具,但市面上絕大多數的ORM對于這種ServerLess數據庫的適配很差,需要解決各種依賴問題。 那么在嘗試了一圈后,發現Drizzle是最好的搭配方案,選擇它最核心的理由是:它沒有三方依賴、且對ServerLess這個場景非常友好。 [Drizzle地址](https://orm.drizzle.team/),建議看文檔,中文只是閱讀起來快一點,精簡一點。
Schema:數據模型定義
Schema 是 Drizzle ORM 的基礎,它定義了數據庫表的結構和關系。
基本表定義
以下是一個簡單的表定義示例:
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'
// 定義用戶表
export const users = sqliteTable('users', {
id: integer('id').primaryKey(),
name: text('name').notNull(),
email: text('email').notNull().unique(),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull().defaultNow()
})
Schema 組織方式
Drizzle 允許你靈活組織 Schema 文件,可以選擇單文件或多文件方式:
單文件方式
適合小型項目,將所有表定義放在一個 schema.ts 文件中:
// src/db/schema.ts
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'
export const users = sqliteTable('users', {
/* 列定義 */
})
export const posts = sqliteTable('posts', {
/* 列定義 */
})
多文件方式
對于大型項目,可以將表定義分散到多個文件中:
?? src
└ ?? db
└ ?? schema
├ ?? users.ts
├ ?? posts.ts
└ ?? comments.ts
命名約定轉換
TypeScript 通常使用駝峰命名法(camelCase),而數據庫常用蛇形命名法(snake_case)。Drizzle 提供了自動轉換功能:
// schema.ts
export const users = sqliteTable('users', {
id: integer('id'),
firstName: text('first_name') // 顯式指定數據庫列名
})
// 或使用自動轉換
const db = drizzle(sqlite, {
schema: { users },
// 自動將 camelCase 轉換為 snake_case
casing: 'snake_case'
})
關系定義
Drizzle 支持定義表之間的關系:
export const posts = sqliteTable('posts', {
id: integer('id').primaryKey(),
title: text('title').notNull(),
content: text('content'),
userId: integer('user_id').references(() => users.id)
})
復用列定義
對于常見的列模式(如時間戳字段),可以創建可復用的定義:
// 通用時間戳字段
const timestamps = {
createdAt: integer('created_at', { mode: 'timestamp' }).notNull().defaultNow(),
updatedAt: integer('updated_at', { mode: 'timestamp' })
}
// 在多個表中復用
export const users = sqliteTable('users', {
id: integer('id').primaryKey(),
// ...其他字段
...timestamps
})
通過這種方式定義 Schema,Drizzle 不僅提供了類型安全的數據訪問,還能自動生成遷移腳本,大大簡化了數據庫管理工作。
數據庫連接
Drizzle ORM 通過數據庫驅動執行 SQL 查詢。
數據庫驅動就是指的一個中間層,負責將查詢請求發送到數據庫并處理返回的結果。Drizzle 支持多種數據庫驅動,使其能夠與各種數據庫系統無縫集成。
基本連接方式
import { drizzle } from 'drizzle-orm/node-postgres'
import { users } from './schema'
// 創建數據庫連接
const db = drizzle(process.env.DATABASE_URL)
// 使用連接執行查詢
const usersCount = await db.$count(users)
Drizzle 的工作流程如下:
-
你的查詢(如
db.$count(users))被轉換為 SQL 語句(SELECT COUNT(*) FROM users) -
SQL 語句通過數據庫驅動發送到數據庫
-
數據庫返回結果,驅動將其轉換為 JavaScript 對象
-
Drizzle 將結果返回給你的應用
訪問底層驅動
如果需要,你可以直接訪問底層數據庫驅動:
import { drizzle } from 'drizzle-orm/node-postgres'
const db = drizzle(process.env.DATABASE_URL)
const pool = db.$client // 訪問底層 node-postgres 驅動
ServerLess 環境支持
Drizzle 原生支持各種邊緣計算和 ServerLess 環境,這也是它與 Cloudflare D1 完美配合的原因:
// Neon 數據庫(ServerLess PostgreSQL)
import { drizzle } from 'drizzle-orm/neon-http'
const db = drizzle(process.env.DATABASE_URL)
// Cloudflare D1
import { drizzle } from 'drizzle-orm/d1'
export default {
async fetch(request, env) {
const db = drizzle(env.DB)
// 使用 db 執行查詢
}
}
特定運行時支持
Drizzle 還支持特定運行時的數據庫驅動:
// Bun SQLite
import { drizzle } from 'drizzle-orm/bun-sqlite'
const db = drizzle() // 創建內存數據庫
// 或
const db = drizzle('./sqlite.db') // 連接文件數據庫
連接 URL 格式
數據庫連接 URL 通常遵循以下格式:
postgresql://username:password@hostname/database_name
例如:
postgresql://alex:AbC123dEf@ep-cool-darkness-123456.us-east-2.aws.neon.tech/dbname
其中:
-
username: 數據庫用戶名 -
password: 數據庫密碼 -
hostname: 數據庫服務器地址 -
database_name: 數據庫名稱
通過這種簡單的連接方式,Drizzle 讓你能夠快速開始使用數據庫,而不必擔心復雜的配置和設置。
數據庫查詢
Drizzle 提供了兩種主要的查詢方式:SQL 風格語法和關系式 API。這兩種方式各有優勢,可以根據不同場景選擇使用。
SQL 風格查詢
Drizzle 的核心理念是"如果你懂 SQL,你就懂 Drizzle"。與其他 ORM 不同,Drizzle 不會抽象掉 SQL,而是擁抱它,提供類似 SQL 的 API:
// 查詢示例
const result = await db.select().from(posts).leftJoin(comments, eq(posts.id, comments.postId)).where(eq(posts.id, 10))
// 生成的 SQL
// SELECT *
// FROM posts
// LEFT JOIN comments ON posts.id = comments.post_id
// WHERE posts.id = 10
基本 CRUD 操作
- 查詢數據
// 查詢所有用戶
const allUsers = await db.select().from(users)
// 查詢特定字段
const userNames = await db.select({ id: users.id, name: users.name }).from(users)
// 條件查詢
import { eq, like } from 'drizzle-orm'
const filteredUsers = await db.select().from(users).where(eq(users.email, 'test@example.com'))
- 插入數據
// 插入單條記錄
await db.insert(users).values({
name: '張三',
email: 'zhangsan@example.com'
})
// 插入多條記錄
await db.insert(users).values([
{ name: '李四', email: 'lisi@example.com' },
{ name: '王五', email: 'wangwu@example.com' }
])
- 更新數據
// 更新記錄
await db.update(users).set({ name: '張三豐' }).where(eq(users.id, 1))
- 刪除數據
// 刪除記錄
await db.delete(users).where(eq(users.id, 1))
關系式查詢 API
對于需要獲取嵌套關系數據的場景,Drizzle 提供了更簡潔的關系式 API:
// 獲取用戶及其所有文章
const usersWithPosts = await db.query.users.findMany({
with: {
posts: true
}
})
// 結果格式
// [
// { id: 1, name: '張三', posts: [{ id: 1, title: '文章1' }, ...] },
// ...
// ]
這種方式特別適合獲取嵌套數據,Drizzle 會自動處理關聯和數據映射,同時保證只生成一條 SQL 查詢,避免 N+1 查詢問題。
高級查詢技巧
Drizzle 支持查詢組合和分區,讓你能夠構建復雜而靈活的查詢:
- 組合條件查詢
// 動態構建查詢條件
function getProductsBy({ name, category, maxPrice }) {
const filters = []
if (name) filters.push(like(products.name, `%${name}%`))
if (category) filters.push(eq(products.category, category))
if (maxPrice) filters.push(lte(products.price, maxPrice))
return db
.select()
.from(products)
.where(filters.length ? and(...filters) : undefined)
}
- 子查詢
// 使用子查詢
const subquery = db.select().from(staff).leftJoin(users, eq(staff.userId, users.id)).as('staff_users')
const result = await db.select().from(tickets).leftJoin(subquery, eq(subquery.staff_users.userId, tickets.assignedTo))
通過這些靈活的查詢方式,Drizzle 既保持了 SQL 的強大表達能力,又提供了更簡潔的 API 來處理常見的數據訪問模式,讓數據庫操作變得既直觀又高效。
數據庫遷移
數據庫遷移是開發過程中的重要環節,Drizzle 通過 Drizzle Kit 工具提供了完整的遷移解決方案。
Drizzle Kit 簡介
Drizzle Kit 是一個命令行工具,用于管理 SQL 數據庫遷移:
npm install -D drizzle-kit
Drizzle Kit 提供了多種命令來滿足不同的遷移需求:
| 命令 | 功能描述 |
generate |
根據 Schema 生成 SQL 遷移文件 |
migrate |
應用生成的 SQL 遷移文件到數據庫 |
push |
直接將 Schema 變更推送到數據庫 |
pull |
從數據庫拉取 Schema 并轉換為 Drizzle Schema |
studio |
啟動 Drizzle Studio 用于可視化數據庫管理 |
check |
檢查生成的遷移文件是否存在沖突 |
up |
升級之前生成的遷移快照 |
配置 Drizzle Kit
Drizzle Kit 通過 drizzle.config.ts 文件進行配置:
// drizzle.config.ts
import { defineConfig } from 'drizzle-kit'
export default defineConfig({
dialect: 'postgresql', // 數據庫類型
schema: './src/db/schema.ts', // Schema 文件路徑
out: './drizzle', // 遷移文件輸出目錄
dbCredentials: {
// 數據庫連接信息(用于 migrate、push、pull 等命令)
url: 'postgresql://user:password@host:port/dbname'
}
})
主要配置項包括:
-
dialect: 數據庫類型('postgresql'、'mysql'、'sqlite' 等) -
schema: Schema 文件路徑,支持 glob 模式匹配多個文件 -
out: 遷移文件輸出目錄,默認為 './drizzle' -
dbCredentials: 數據庫連接信息 -
migrations: 遷移相關配置,如遷移記錄表名稱
遷移工作流
1. 生成遷移文件 (generate)
當你修改 Schema 后,可以生成遷移文件:
npx drizzle-kit generate
這將在 out 目錄中生成 SQL 遷移文件,包含從當前數據庫狀態到新 Schema 的所有必要更改。
你可以通過 --name 參數指定遷移文件的名稱:
npx drizzle-kit generate --name=init
這將生成類似 0000_init.sql 的文件。
對于需要自定義 SQL 操作(如數據填充)的場景,可以生成空白遷移文件:
npx drizzle-kit generate --custom --name=seed-users
然后在生成的文件中添加自定義 SQL:
-- ./drizzle/0001_seed-users.sql
INSERT INTO "users" ("name") VALUES('張三');
INSERT INTO "users" ("name") VALUES('李四');
2. 應用遷移 (migrate)
生成遷移文件后,可以將其應用到數據庫:
npx drizzle-kit migrate
Drizzle Kit 會在數據庫中創建一個名為 __drizzle_migrations 的表,用于記錄已應用的遷移。你可以自定義這個表:
// drizzle.config.ts
export default defineConfig({
// ...其他配置
migrations: {
table: 'my_migrations', // 默認為 __drizzle_migrations
schema: 'public' // PostgreSQL 專用,默認為 drizzle
}
})
3. 直接推送 Schema (push)
在開發環境中,可以直接將 Schema 變更推送到數據庫,跳過生成遷移文件的步驟:
npx drizzle-kit push
這個命令會分析當前數據庫狀態和 Schema 文件的差異,并直接應用變更,適合快速迭代的開發階段。
4. 從數據庫拉取 Schema (pull)
如果你有一個現有的數據庫,可以從中拉取 Schema 并轉換為 Drizzle Schema:
npx drizzle-kit pull
這對于將現有項目遷移到 Drizzle 特別有用。
多環境配置
對于有多個環境(開發、測試、生產)的項目,可以創建多個配置文件:
?? 項目根目錄
├ ?? drizzle-dev.config.ts
├ ?? drizzle-prod.config.ts
使用時指定配置文件:
npx drizzle-kit push --config=drizzle-dev.config.ts
Drizzle Studio
Drizzle Kit 還提供了一個可視化工具 Drizzle Studio,用于瀏覽和管理數據庫:
npx drizzle-kit studio
這將啟動一個本地服務器,通過瀏覽器界面可以查看表結構、數據記錄,并執行基本的 CRUD 操作。
完整遷移流程示例
以下是一個完整的遷移流程示例:
- 定義 Schema:
// src/schema.ts
import { pgTable, serial, text } from 'drizzle-orm/pg-core'
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull()
})
- 配置 Drizzle Kit:
// drizzle.config.ts
import { defineConfig } from 'drizzle-kit'
export default defineConfig({
dialect: 'postgresql',
schema: './src/schema.ts',
dbCredentials: {
url: 'postgresql://user:password@host:port/dbname'
}
})
- 生成遷移文件:
npx drizzle-kit generate --name=init
- 應用遷移:
npx drizzle-kit migrate
完整配置示例
以下是一個包含所有可用選項的擴展配置示例:
import { defineConfig } from 'drizzle-kit'
export default defineConfig({
out: './drizzle', // 遷移文件輸出目錄
dialect: 'postgresql', // 數據庫類型
schema: './src/schema.ts', // Schema 文件路徑
driver: 'pglite', // 特定數據庫驅動
dbCredentials: {
// 數據庫連接信息
url: './database/'
},
extensionsFilters: ['postgis'], // 忽略特定擴展的表
schemaFilter: 'public', // 要管理的 schema
tablesFilter: '*', // 要管理的表
introspect: {
// pull 命令的配置
casing: 'camel' // 列名命名風格
},
migrations: {
// 遷移記錄配置
prefix: 'timestamp', // 遷移文件前綴
table: '__drizzle_migrations__', // 遷移記錄表名
schema: 'public' // 遷移記錄表所在 schema
},
entities: {
// 實體管理配置
roles: {
// 角色管理
provider: '', // 數據庫提供商
exclude: [], // 排除的角色
include: [] // 包含的角色
}
},
breakpoints: true, // 是否在 SQL 中添加斷點
strict: true, // push 命令是否需要確認
verbose: true // 是否打印詳細日志
})
多配置文件支持
對于管理多個數據庫或環境的項目,可以創建多個配置文件:
?? 項目根目錄
├ ?? drizzle-dev.config.ts
├ ?? drizzle-prod.config.ts
使用時指定配置文件:
npx drizzle-kit generate --config=drizzle-dev.config.ts
主要配置項詳解
-
dialect:數據庫類型
-
可選值:
postgresql、mysql、sqlite、turso、singlestore -
適用命令:
generate、migrate、push、pull、check、up
-
-
schema:Schema 文件路徑
-
類型:
string | string[](支持 glob 模式) -
適用命令:
generate、push -
示例:
'./src/schema.ts'或'./src/schema/*.ts'
-
-
out:遷移文件輸出目錄
-
類型:
string -
默認值:
'drizzle' -
適用命令:
generate、migrate、push、pull、check、up
-
-
dbCredentials:數據庫連接信息
-
類型:URL 字符串或連接參數對象
-
適用命令:
migrate、push、pull -
示例:
dbCredentials: { url: 'postgresql://user:password@host:port/db' } // 或 dbCredentials: { host: 'host', port: 5432, user: 'user', password: 'password', database: 'dbname', ssl: true }
-
-
migrations:遷移記錄配置
-
類型:
{ table: string, schema: string } -
默認值:
{ table: '__drizzle_migrations', schema: 'drizzle' } -
適用命令:
migrate
-
-
tablesFilter:表過濾器
-
類型:
string | string[] -
適用命令:
generate、push、pull -
示例:
['users', 'posts', 'project1_*']
-
-
schemaFilter:Schema 過濾器
-
類型:
string[] -
默認值:
['public'] -
適用命令:
generate、push、pull
-
-
extensionsFilters:擴展過濾器
-
類型:
string[] -
默認值:
[] -
適用命令:
push、pull -
示例:
['postgis'](忽略 PostGIS 擴展創建的表)
-
-
entities.roles:角色管理配置
-
類型:
boolean | { provider: string, include: string[], exclude: string[] } -
默認值:
false -
適用命令:
push、pull、generate -
示例:
entities: { roles: { provider: 'supabase', // 使用 Supabase 預定義角色 exclude: ['admin'] // 排除 admin 角色 } }
-
-
strict:嚴格模式
2. 類型:`boolean`
3. 默認值:`false`
4. 適用命令:`push`
5. 作用:執行 `push` 命令時是否需要確認 SQL 語句
- verbose:詳細日志
2. 類型:`boolean`
3. 默認值:`true`
4. 適用命令:`generate`、`pull`
5. 作用:是否打印詳細的 SQL 語句
- breakpoints:SQL 斷點
2. 類型:`boolean`
3. 默認值:`true`
4. 適用命令:`generate`、`pull`
5. 作用:是否在生成的 SQL 中添加 `--> statement-breakpoint` 斷點(對于不支持在一個事務中執行多個 DDL 語句的數據庫如 MySQL 和 SQLite 很重要)
結束
講這個的主要目的是為了給大家普及一下海外批量應用的基礎套件的知識,歡迎更多的了解下。
浙公網安備 33010602011771號