依赖安装
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
来进行数据库填充了