drizzle-orm-v0
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSkill: drizzle-orm
Skill:drizzle-orm
Scope
范围
- Applies to: Drizzle ORM v0.45+ for PostgreSQL, MySQL, SQLite - schema definitions, type-safe queries, migrations
- Does NOT cover: Database driver setup, connection pooling configuration, other ORMs
- 适用于:Drizzle ORM v0.45+ for PostgreSQL、MySQL、SQLite——schema 定义、类型安全查询、迁移
- 不涵盖:数据库驱动配置、连接池配置、其他 ORM
Assumptions
前提条件
- Drizzle ORM v0.45+
- Drizzle Kit v0.31+ (dev dependency) for migrations
- PostgreSQL, MySQL, or SQLite database
- TypeScript v5+ with strict mode
- ESM module system
- Drizzle ORM v0.45+
- Drizzle Kit v0.31+(开发依赖),用于迁移
- PostgreSQL、MySQL 或 SQLite 数据库
- TypeScript v5+,启用 strict 模式
- ESM 模块系统
Principles
原则
- Schemas defined using table builders (,
pgTable,mysqlTable) with typed columnssqliteTable - Column types match database constraints (with length,
varcharwith mode)timestamp - Indexes defined in table definition second parameter using helper
index() - Text primary keys with for foreign keys are a valid default
.references() - Query helpers (,
eq,and,or) provide type-safe SQL constructionlike - Type inference via and
$inferSelecteliminates manual types$inferInsert - Migrations generated with (not
drizzle-kit generatein production)push - Prepared statements optimize frequently executed queries
- Schemas organized by domain (one file per entity/table)
- Transactions () ensure atomic multi-step operations
db.transaction
- 使用表构建器(、
pgTable、mysqlTable)和类型化列定义 schemasqliteTable - 列类型与数据库约束匹配(包含长度,
varchar包含 mode)timestamp - 在表定义的第二个参数中使用 辅助函数定义索引
index() - 文本主键配合 定义外键,是有效的默认方案
.references() - 查询辅助函数(、
eq、and、or)提供类型安全的 SQL 构建能力like - 通过 和
$inferSelect推断类型,无需手动定义类型$inferInsert - 使用 生成迁移(生产环境中不要使用
drizzle-kit generate)push - 预处理语句可优化频繁执行的查询
- 按领域组织 schema(每个实体/表一个文件)
- 事务()可确保多步操作的原子性
db.transaction
Constraints
约束
MUST
必须
- Use Drizzle Kit for migrations (,
drizzle-kit generate)drizzle-kit migrate - Define column types matching database constraints
- Use query helpers instead of raw SQL
- 使用 Drizzle Kit 进行迁移(、
drizzle-kit generate)drizzle-kit migrate - 定义与数据库约束匹配的列类型
- 使用查询辅助函数,而不是原始 SQL
SHOULD
应该
- Use transactions for multi-step operations
- Use prepared statements for frequently executed queries
- Export types via and
$inferSelect$inferInsert - Handle for structured error handling
DrizzleQueryError - Organize schemas by domain (one file per entity)
- Use selective field loading (not full rows)
- Specify length for columns
varchar - Use helper in table definitions
index() - Use PGLite for testing PostgreSQL schemas
- Use and relational query builder when the project already uses them
relations()
- 对多步操作使用事务
- 对频繁执行的查询使用预处理语句
- 通过 和
$inferSelect导出类型$inferInsert - 处理 ,实现结构化错误处理
DrizzleQueryError - 按领域组织 schema(每个实体一个文件)
- 使用选择性字段加载,而不是加载完整行
- 为 列指定长度
varchar - 在表定义中使用 辅助函数
index() - 使用 PGLite 测试 PostgreSQL schema
- 当项目已经使用 和关系查询构建器时,继续沿用
relations()
OPTIONAL
可选
- Identity columns () instead of
generatedAlwaysAsIdentityin PostgreSQLserial - Relational query builder () for complex joins when relations are defined
db.query.*
- 在 PostgreSQL 中使用 Identity 列(),而不是
generatedAlwaysAsIdentityserial - 定义 relations 后,使用关系查询构建器()处理复杂 join
db.query.*
AVOID
避免
- Raw SQL unless necessary
- Manual type assertions (use inferred types)
- Skipping migration generation
- in new PostgreSQL tables (use identity columns)
serial - Over-indexing (index only where queries justify)
- Fetching full rows when only few columns needed
- in production (use
push+generate)migrate - String-based timestamp mode when DB supports date/time types
- 除非必要,避免使用原始 SQL
- 避免手动类型断言(使用推断类型)
- 避免跳过迁移生成
- 在新的 PostgreSQL 表中避免使用 (使用 Identity 列)
serial - 避免过度创建索引(仅在查询需要时创建索引)
- 仅需少量字段时,避免获取完整行
- 在生产环境中避免使用 (使用
push+generate)migrate - 当数据库支持日期/时间类型时,避免使用字符串形式的 timestamp mode
Interactions
交互关系
- Works with next Server Components and API routes
- Complements fastify for API development
- 可与 next 的 Server Components 和 API routes 配合使用
- 与 fastify 互补,适合 API 开发
Patterns
模式
Schema Definition
Schema 定义
typescript
import { index, pgTable, text, timestamp, varchar } from 'drizzle-orm/pg-core'
export const users = pgTable(
'users',
{
id: text('id').primaryKey(),
email: varchar('email', { length: 255 }).notNull().unique(),
createdAt: timestamp('created_at').defaultNow().notNull(),
updatedAt: timestamp('updated_at').defaultNow().notNull(),
},
table => [index('users_email_idx').on(table.email)],
)
export type User = typeof users.$inferSelect
export type NewUser = typeof users.$inferInserttypescript
import { index, pgTable, text, timestamp, varchar } from 'drizzle-orm/pg-core'
export const users = pgTable(
'users',
{
id: text('id').primaryKey(),
email: varchar('email', { length: 255 }).notNull().unique(),
createdAt: timestamp('created_at').defaultNow().notNull(),
updatedAt: timestamp('updated_at').defaultNow().notNull(),
},
table => [index('users_email_idx').on(table.email)],
)
export type User = typeof users.$inferSelect
export type NewUser = typeof users.$inferInsertIdentity Columns
Identity 列
typescript
import { pgTable, integer, generatedAlwaysAsIdentity } from 'drizzle-orm/pg-core'
export const posts = pgTable('posts', {
id: integer('id').primaryKey().generatedAlwaysAsIdentity(),
})typescript
import { pgTable, integer, generatedAlwaysAsIdentity } from 'drizzle-orm/pg-core'
export const posts = pgTable('posts', {
id: integer('id').primaryKey().generatedAlwaysAsIdentity(),
})Query Builder
查询构建器
typescript
import { eq } from 'drizzle-orm'
const user = await db
.select()
.from(users)
.where(eq(users.id, userId))
.limit(1)
const userWithPosts = await db.query.users.findFirst({
where: eq(users.id, userId),
with: { posts: true },
})
const userEmail = await db
.select({ email: users.email })
.from(users)
.where(eq(users.id, userId))typescript
import { eq } from 'drizzle-orm'
const user = await db
.select()
.from(users)
.where(eq(users.id, userId))
.limit(1)
const userWithPosts = await db.query.users.findFirst({
where: eq(users.id, userId),
with: { posts: true },
})
const userEmail = await db
.select({ email: users.email })
.from(users)
.where(eq(users.id, userId))Transactions
事务
typescript
await db.transaction(async (tx) => {
const [user] = await tx.insert(users).values(userData).returning()
await tx.insert(profiles).values({ userId: user.id, ...profileData })
})typescript
await db.transaction(async (tx) => {
const [user] = await tx.insert(users).values(userData).returning()
await tx.insert(profiles).values({ userId: user.id, ...profileData })
})Prepared Statements
预处理语句
typescript
import { placeholder } from 'drizzle-orm'
const getUserByEmail = db
.select()
.from(users)
.where(eq(users.email, placeholder('email')))
.prepare('get_user_by_email')
const user = await getUserByEmail.execute({ email: 'user@example.com' })typescript
import { placeholder } from 'drizzle-orm'
const getUserByEmail = db
.select()
.from(users)
.where(eq(users.email, placeholder('email')))
.prepare('get_user_by_email')
const user = await getUserByEmail.execute({ email: 'user@example.com' })Error Handling
错误处理
typescript
import { DrizzleQueryError } from 'drizzle-orm'
try {
const user = await db.select().from(users).where(eq(users.id, userId))
} catch (error) {
if (error instanceof DrizzleQueryError) {
if (error.cause?.code === '23505') {
throw new Error('User already exists')
}
}
throw error
}typescript
import { DrizzleQueryError } from 'drizzle-orm'
try {
const user = await db.select().from(users).where(eq(users.id, userId))
} catch (error) {
if (error instanceof DrizzleQueryError) {
if (error.cause?.code === '23505') {
throw new Error('User already exists')
}
}
throw error
}Relations
关系
typescript
import { relations } from 'drizzle-orm'
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}))
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
}),
}))typescript
import { relations } from 'drizzle-orm'
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}))
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
}),
}))Database Connection
数据库连接
typescript
import { drizzle } from 'drizzle-orm/node-postgres'
import { Pool } from 'pg'
import * as schema from './schema'
const pool = new Pool({ connectionString: process.env.DATABASE_URL })
export const db = drizzle(pool, { schema })typescript
import { drizzle } from 'drizzle-orm/node-postgres'
import { Pool } from 'pg'
import * as schema from './schema'
const pool = new Pool({ connectionString: process.env.DATABASE_URL })
export const db = drizzle(pool, { schema })Drizzle Kit Config
Drizzle Kit 配置
typescript
import { defineConfig } from 'drizzle-kit'
export default defineConfig({
dialect: 'postgresql',
schema: './src/db/schema/index.ts',
out: './src/db/migrations',
dbCredentials: { url: process.env.DATABASE_URL! },
migrations: {
table: '__drizzle_migrations',
schema: 'public',
},
verbose: true,
strict: true,
})typescript
import { defineConfig } from 'drizzle-kit'
export default defineConfig({
dialect: 'postgresql',
schema: './src/db/schema/index.ts',
out: './src/db/migrations',
dbCredentials: { url: process.env.DATABASE_URL! },
migrations: {
table: '__drizzle_migrations',
schema: 'public',
},
verbose: true,
strict: true,
})References
参考资料
- Query Patterns - CRUD operations, joins, aggregations
- PostgreSQL Patterns - PostgreSQL-specific patterns
- 查询模式——CRUD 操作、join、聚合
- PostgreSQL 模式——PostgreSQL 专用模式