依赖安装
pnpm add drizzle-orm pg dotenv pnpm add -D drizzle-kit tsx @types/pg
准备 DrizzleModule
这里我们都在 /src/drizzle
目录中操作
创建 drizzle.provider.ts
文件
import { Provider } from '@nestjs/common'; import { drizzle, NodePgDatabase } from 'drizzle-orm/node-postgres'; import { Pool } from 'pg'; import { ConfigService } from '@/config/config.service'; import { Schema, schema } from './schema'; export const PG_CONNECTION = 'PG_CONNECTION'; export const DrizzleProvider: Provider = { provide: PG_CONNECTION, inject: [ConfigService], useFactory(configService: ConfigService) { const connectionString = configService.database.DATABASE_URL; const pool = new Pool({ connectionString }); return drizzle(pool, { schema, logger: true }) as NodePgDatabase<Schema>; }, };
创建 drizzle.service.ts
文件
有了这个以后,我们可以在后续的模块中直接注入该模块 constructor(private readonly drizzle: DrizzleService) {}
,而不是每次都要写 constructor(@Inject(PG_CONNECTION) readonly db: NodePgDatabase<Schema>) {}
这么一长串内容
import { Inject, Injectable } from '@nestjs/common'; import { NodePgDatabase } from 'drizzle-orm/node-postgres'; import type { Schema } from './schema'; import { PG_CONNECTION } from './drizzle.provider'; @Injectable() export class DrizzleService { constructor(@Inject(PG_CONNECTION) readonly db: NodePgDatabase<Schema>) {} }
创建 drizzle.module.ts
文件
import { Global, Module } from '@nestjs/common'; import { DrizzleProvider } from './drizzle.provider'; import { DrizzleService } from './drizzle.service'; @Global() @Module({ imports: [], providers: [DrizzleService, DrizzleProvider], exports: [DrizzleService], }) export class DrizzleModule {}
创建数据库 Schema
创建一个 user.entity.ts
import { relations } from 'drizzle-orm'; import { pgTable, serial, timestamp, varchar } from 'drizzle-orm/pg-core'; import { createSelectSchema } from 'drizzle-zod'; import { z } from 'zod/v4'; export const timestamps = { createdAt: timestamp('created_at').notNull().defaultNow(), updatedAt: timestamp('updated_at').notNull().defaultNow(), }; export const user = pgTable('user', { id: serial().primaryKey(), username: varchar().notNull().unique(), password: varchar().notNull(), ...timestamps, }); export const selectUserSchema = createSelectSchema(user); export type SelectUser = z.infer<typeof selectUserSchema>;
创建 schema.ts
文件
import { user } from './user.entity'; export type { SelectUser } from './user.entity'; export const schema = { user } export type Schema = typeof schema; export type SchemaName = keyof Schema;
这样我们的 DrizzleModule 就准备好了
drizzle 相关的配置
我们要在项目根目录下创建 drizzle.config.ts
文件,这个文件是 DrizzleOrm 的配置文件
import 'dotenv/config'; import { defineConfig } from 'drizzle-kit'; export default defineConfig({ schema: './src/drizzle/schema/**.entity.ts', // 这里是数据库 schema 文件的位置 out: './drizzle/migrations', // 数据库迁移文件生成的地址 dialect: 'postgresql', // 数据库驱动 dbCredentials: { url: process.env.DATABASE_URL!, }, });
package.json 中的脚本
{ "scripts": { "db:generate": "drizzle-kit generate", "db:migrate": "drizzle-kit migrate", "db:push": "drizzle-kit push", "db:studio": "drizzle-kit studio", }, }
- db:generate 声明时或后续 Schema 更改时基于 Drizzle Schema 生成 SQL 迁移
- db:migrate 运行迁移后,Drizzle Kit 会将成功应用的迁移记录保存到数据库中。
- db:push 允许您直接将您的架构和后续架构更改推送到数据库
- db:studio 本地数据库可视化面板
生成 schema 并将改动同步到数据库
pnpm run db:generate
pnpm run db:migrate
成功后我们可以使用 pnpm run db:studio
查看数据库中的内容。
在 app.module.ts 中引入 DrizzleModule
@Module({ imports: [ // ... DrizzleModule, ], }) export class AppModule {}
在模块中使用,这里用 UserModule 作为例子
@Injectable() export class UserService { constructor(private readonly drizzle: DrizzleService) {} async create(createUserDto: CreateUserDto) { const result = await this.drizzle.db.insert(schema.user).values(createUserDto).returning(); return result; } async findOne(id: number) { const [user] = await this.drizzle.db.select().from(schema.user).where(eq(schema.user.id, id)); return user; } async update(id: number, updateUserDto: UpdateUserDto) { const user = this.drizzle.db .update(schema.user) .set(updateUserDto) .where(eq(schema.user.id, id)); return user; } async remove(id: number) { const [user] = await this.drizzle.db .delete(schema.user) .where(eq(schema.user.id, id)) .returning(); return user; } }
附加内容
vscode 插件推荐
vscode-drizzle-orm
这个插件可以让我们看到 drizzle 生成好的数据库模型图
数据库填充
在开发中我们通常都要填充一些模拟数据用来测试,那么在 nestjs + drizzle 这个组合下我们如何操作呢?
我们还是在 src/drizzle
文件夹中操作
创建 db.ts
import 'dotenv/config'; import { drizzle, NodePgDatabase } from 'drizzle-orm/node-postgres'; import { Pool } from 'pg'; import { Schema, schema } from './schema'; const connectionString = process.env.DATABASE_URL; if (!connectionString) { throw new Error('DATABASE_URL is not defined'); } const pool = new Pool({ connectionString }); export const db: NodePgDatabase<Schema> = drizzle(pool, { schema, logger: true }); export type db = NodePgDatabase<Schema>;
创建 seed 相关文件
import 'dotenv/config'; import { Table } from 'drizzle-orm'; import { db } from './db'; import { schema } from './schema'; import { seedUser } from './seeds/user.seed'; // 清空数据库中的数据 async function clearTable() { // eslint-disable-next-line drizzle/enforce-delete-with-where return await Promise.all(Object.values(schema).map((table: Table) => db.delete(table).execute())); } async function seed() { // 每次填充前先将数据库中的数据清空 await clearTable(); // 填充一些测试数据用来使用 await seedUser(db); } seed().catch((e) => { console.error(e); process.exit(0); });
import { hashSync } from 'bcrypt'; import { db } from '@/drizzle/db'; import { schema } from '../schema'; export async function seedUser(db: db) { await db.insert(schema.user).values([ { username: 'admin', password: hashSync('123456', 10) }, { username: 'user', password: hashSync('123456', 10) }, ]); }
在 package.json 中添加命令
{ "db:seed": "ts-node ./src/drizzle/seed.ts", }
我们就可以运行 pnpm run db:seed
来进行数据库填充了