在 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 来进行数据库填充了

相关推荐
程序员码歌2 小时前
短思考第261天,浪费时间的十个低效行为,看看你中了几个?
前端·ai编程
Swift社区3 小时前
React Navigation 生命周期完整心智模型
前端·react.js·前端框架
若梦plus3 小时前
从微信公众号&小程序的SDK剖析JSBridge
前端
用泥种荷花4 小时前
Python环境安装
前端
Light604 小时前
性能提升 60%:前端性能优化终极指南
前端·性能优化·图片压缩·渲染优化·按需拆包·边缘缓存·ai 自动化
Jimmy4 小时前
年终总结 - 2025 故事集
前端·后端·程序员
烛阴4 小时前
C# 正则表达式(2):Regex 基础语法与常用 API 全解析
前端·正则表达式·c#
roman_日积跬步-终至千里4 小时前
【人工智能导论】02-搜索-高级搜索策略探索篇:从约束满足到博弈搜索
java·前端·人工智能
GIS之路4 小时前
GIS 数据转换:使用 GDAL 将 TXT 转换为 Shp 数据
前端
多看书少吃饭4 小时前
从Vue到Nuxt.js
前端·javascript·vue.js