在 Nestjs 中使用 Drizzle ORM

依赖安装

shell 复制代码
pnpm add drizzle-orm pg dotenv
pnpm add -D drizzle-kit tsx @types/pg

准备 DrizzleModule

这里我们都在 /src/drizzle 目录中操作

创建 drizzle.provider.ts 文件

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>) {} 这么一长串内容

ts 复制代码
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 文件

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

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 文件

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 的配置文件

ts 复制代码
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 中的脚本

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 并将改动同步到数据库

shell 复制代码
pnpm run db:generate
shell 复制代码
pnpm run db:migrate

成功后我们可以使用 pnpm run db:studio 查看数据库中的内容。

在 app.module.ts 中引入 DrizzleModule

ts 复制代码
@Module({
    imports: [
    // ...
        DrizzleModule,
    ],
})
export class AppModule {}

在模块中使用,这里用 UserModule 作为例子

ts 复制代码
@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

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 相关文件

ts 复制代码
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);
});
ts 复制代码
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 中添加命令

json 复制代码
{
  "db:seed": "ts-node ./src/drizzle/seed.ts",
}

我们就可以运行 pnpm run db:seed 来进行数据库填充了

相关推荐
Hilaku8 小时前
就因为package.json里少了个^号,我们公司赔了客户十万块
前端·javascript·npm
晴殇i8 小时前
尤雨溪创立的 VoidZero 完成 1250 万美元 A 轮融资,加速整合前端工具链生态
前端·vue.js
一大树8 小时前
MutationObserver 完整用法指南
前端
一晌小贪欢8 小时前
【Html模板】赛博朋克风格数据分析大屏(已上线-可预览)
前端·数据分析·html·数据看板·看板·电商大屏·大屏看板
墨寒博客栈8 小时前
Linux基础常用命令
java·linux·运维·服务器·前端
野生龟9 小时前
designable和formily实现简单的低代码平台学习
前端
路多辛9 小时前
为什么我要做一个开发者工具箱?聊聊 Kairoa 的诞生
前端·后端
jerryinwuhan9 小时前
理论及算法_时间抽取论文
前端·算法·easyui
秋子aria9 小时前
模块的原理及使用
前端·javascript
菜市口的跳脚长颌9 小时前
一个 Vite 打包配置,引发的问题—— global: 'globalThis'
前端·vue.js·vite