Skip to content

数据库管理

IAM 项目使用 Drizzle ORM 和 PostgreSQL 进行数据管理。

数据库架构定义在 packages/db/src/schema/ 目录下。

使用 Drizzle 定义表结构:

import { pgTable, text, timestamp, boolean } from 'drizzle-orm/pg-core'
export const posts = pgTable('posts', {
id: text('id').primaryKey(),
title: text('title').notNull(),
content: text('content'),
authorId: text('author_id').references(() => user.id),
published: boolean('published').default(false),
createdAt: timestamp('created_at').defaultNow(),
updatedAt: timestamp('updated_at')
.defaultNow()
.$onUpdate(() => new Date())
})
  • text(): 文本字段
  • timestamp(): 时间戳字段
  • boolean(): 布尔字段
  • integer(): 整数字段
  • numeric(): 数值字段
  • .primaryKey(): 主键
  • .notNull(): 非空
  • .default(): 默认值
  • .unique(): 唯一约束
  • .references(): 外键引用

使用 relations 定义表之间的关系:

import { relations } from 'drizzle-orm'
// 一对多关系
export const userRelations = relations(user, ({ many }) => ({
posts: many(posts)
}))
// 多对一关系
export const postsRelations = relations(posts, ({ one }) => ({
author: one(user, {
fields: [posts.authorId],
references: [user.id]
})
}))
  • one: 一对一或多对一
  • many: 一对多或多对多
import { db } from '@IAM/db'
import { posts } from '@IAM/db/schema'
// 查询所有记录
const allPosts = await db.select().from(posts)
// 查询单条记录
const [post] = await db.select().from(posts).where(eq(posts.id, postId))
import { eq, and, or, like } from 'drizzle-orm'
// 等值查询
const userPosts = await db.select().from(posts).where(eq(posts.authorId, userId))
// 多条件查询
const publishedPosts = await db
.select()
.from(posts)
.where(and(eq(posts.authorId, userId), eq(posts.published, true)))
// 模糊查询
const searchResults = await db
.select()
.from(posts)
.where(like(posts.title, `%${keyword}%`))
// 插入单条记录
await db.insert(posts).values({
id: generateId(),
title: 'New Post',
content: 'Content here',
authorId: userId
})
// 插入多条记录
await db.insert(posts).values([
{ id: '1', title: 'Post 1', authorId: userId },
{ id: '2', title: 'Post 2', authorId: userId }
])
// 插入并返回
const [newPost] = await db
.insert(posts)
.values({
id: generateId(),
title: 'New Post',
authorId: userId
})
.returning()
import { eq } from 'drizzle-orm'
// 更新记录
await db.update(posts).set({ title: 'Updated Title' }).where(eq(posts.id, postId))
// 更新并返回
const [updatedPost] = await db
.update(posts)
.set({ title: 'Updated Title' })
.where(eq(posts.id, postId))
.returning()
// 删除记录
await db.delete(posts).where(eq(posts.id, postId))
// 删除并返回
const [deletedPost] = await db.delete(posts).where(eq(posts.id, postId)).returning()
import { db } from '@IAM/db'
import { posts, user } from '@IAM/db/schema'
// 使用 join 查询
const postsWithAuthor = await db
.select({
post: posts,
author: user
})
.from(posts)
.innerJoin(user, eq(posts.authorId, user.id))

开发、测试和生产环境都应优先使用可提交、可审查的 migration:

Terminal window
# 生成迁移文件
pnpm run db:generate
# 应用迁移
pnpm run db:migrate

如果调整 Better Auth 插件或认证字段,先固定使用 1.7.1 生成认证 schema:

Terminal window
pnpm dlx auth@1.7.1 generate --yes \
--config packages/auth/src/index.ts \
--output packages/db/src/schema/auth.ts
pnpm db:generate
pnpm db:migrate

DATABASE_URL 用于运行时访问;DATABASE_URL_DIRECT 用于 migration/DDL,未配置时 Drizzle 会回退到 DATABASE_URL

pnpm db:push 会直接修改数据库,可能造成 schema、migration 与真实数据库漂移,只允许在可丢弃的本地数据库临时验证。正式变更必须回到 migration 流程。

使用 Drizzle Studio 可视化管理数据库:

Terminal window
pnpm run db:studio

这将打开一个 Web 界面,可以:

  • 查看表结构
  • 浏览和编辑数据
  • 执行 SQL 查询
  1. 使用迁移: 所有正式变更都生成并提交 migration
  2. 索引优化: 为常用查询字段添加索引
  3. 关系完整性: 使用外键确保数据完整性
  4. 类型安全: 利用 Drizzle 的类型推断
  5. 查询优化: 避免 N+1 查询问题