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))

使用 db:push 快速推送架构变更:

Terminal window
# 推送架构变更到数据库
pnpm run db:push

注意: db:push 会直接修改数据库,适合开发环境。

使用迁移文件管理数据库变更:

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

如果在 packages/auth/src/index.ts 中为 Better-Auth 新增了插件(例如 admin()),在执行以上命令之前,请先按照认证系统文档中的“启用插件后的数据库更新流程”重新生成认证相关的 Schema 并创建迁移文件,再在这里运行 db:generate / db:migrate 将这些变更应用到数据库。

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

Terminal window
pnpm run db:studio

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

  • 查看表结构
  • 浏览和编辑数据
  • 执行 SQL 查询
  1. 使用迁移: 生产环境使用迁移文件而不是 db:push
  2. 索引优化: 为常用查询字段添加索引
  3. 关系完整性: 使用外键确保数据完整性
  4. 类型安全: 利用 Drizzle 的类型推断
  5. 查询优化: 避免 N+1 查询问题