NestJS + DrizzleORM:轻量级、高性能的完美搭配? 🚀

NestJS与DrizzleORM:绝佳的搭配

NestJS是一个用于构建高效、可靠的服务器端应用程序的渐进式Node.js框架,而DrizzleORM是一个轻量级、类型安全的ORM(对象关系映射)工具。本文将探讨这两种技术如何完美地配合使用,以创建强大且可维护的后端应用程序。

Node.js中主流ORM框架的比较

在Node.js生态系统中,开发者有多种ORM选择,其中最流行的包括TypeORM、Prisma和DrizzleORM。下面是这三种框架的详细比较:

各ORM框架优缺点对比

特性 TypeORM Prisma DrizzleORM
成熟度 高,生态系统成熟 中等,快速发展中 较低,相对较新
性能 中等 良好 优秀,极其轻量级
类型安全 基本支持 优秀 优秀,完全类型安全
查询方式 ORM优先,支持原生SQL Prisma查询语言 SQL优先
学习曲线 中等 陡峭 平缓
社区支持 广泛 活跃 发展中
与NestJS集成 官方支持 第三方集成 简单直接
数据库支持 多种数据库 多种数据库 多种数据库
启动时间 较慢 中等 快速
迁移工具 内置 强大 简单有效

优缺点详细分析

TypeORM
优点 缺点
成熟稳定,使用广泛 性能开销较大
与NestJS有官方集成 类型安全性不如新一代ORM工具
支持装饰器,类Java风格 文档有时不够清晰
丰富的关系映射功能 "魔术行为"导致调试困难
完善的社区和资源 大型项目中性能可能成为瓶颈
Prisma
优点 缺点
出色的类型安全性 需要额外的Prisma CLI和运行时
强大的数据迁移工具 对于复杂查询,灵活性可能不足
直观的模式定义语言 学习曲线较陡峭
优秀的查询性能 与NestJS集成需要额外工作
Prisma Studio提供可视化工具 依赖Prisma生态系统
DrizzleORM
优点 缺点
极其轻量级,性能开销最小 相对较新,生态系统仍在发展中
完全类型安全 文档可能不如成熟框架全面
SQL优先,查询直观透明 高级功能可能不如其他框架完善
没有"魔术"方法,行为可预测 市场占有率较低
启动时间快,无需额外代码生成 现成的集成示例相对较少

为什么选择DrizzleORM与NestJS集成?

理由 说明
类型安全与开发体验 DrizzleORM与NestJS都高度重视TypeScript和类型安全,提供一流的开发体验
性能优势 DrizzleORM的轻量级设计减少了性能开销,特别适合微服务架构
透明性和可控性 SQL优先方法与NestJS的明确架构理念相契合,提供更好的控制
可测试性 两种技术都设计为易于测试,适合TDD团队
迁移友好 直观设计和最小化的魔术方法使从其他ORM迁移更加顺畅
适合现代应用需求 对于需要灵活性、性能和类型安全的现代应用,DrizzleORM是更贴合的选择

为什么选择NestJS?

NestJS采用了Angular的许多设计理念,包括模块化架构、依赖注入和装饰器的广泛使用。这使得它成为构建企业级应用程序的理想选择,特别是对于喜欢TypeScript和强类型系统的开发人员。

NestJS的主要优势包括:

  • 基于TypeScript,提供出色的类型安全和开发体验
  • 模块化架构,促进代码组织和重用
  • 内置依赖注入系统,简化测试和维护
  • 支持多种传输层(HTTP、WebSockets、gRPC等)
  • 丰富的生态系统和出色的文档

DrizzleORM简介

DrizzleORM是一个相对较新但快速发展的ORM工具,专为TypeScript设计。与其他ORM相比,DrizzleORM有几个显著特点:

  • 极其轻量级,对项目的性能影响最小
  • 完全类型安全,提供卓越的开发体验
  • 支持多种数据库(PostgreSQL、MySQL、SQLite等)
  • 采用SQL优先的方法,让开发人员保持对查询的控制
  • 没有魔术方法,代码行为透明且可预测

在NestJS项目中集成DrizzleORM

让我们来看看如何在NestJS应用程序中设置和使用DrizzleORM。我们将创建一个简单的用户管理系统,展示这种集成。

步骤1:安装必要的依赖

shell 复制代码
npm install drizzle-orm postgres @nestjs/config
npm install -D drizzle-kit pg

步骤2:创建数据库配置

首先,我们需要设置数据库连接。创建一个drizzle.config.ts文件:

ts 复制代码
import type { Config } from 'drizzle-kit';
import * as dotenv from 'dotenv';

dotenv.config();

export default {
  schema: './src/db/schema/*',
  out: './drizzle',
  driver: 'pg',
  dbCredentials: {
    connectionString: process.env.DATABASE_URL,
  },
} satisfies Config;

步骤3:定义数据库模式

创建一个用户模式文件 src/db/schema/users.ts

ts 复制代码
import { pgTable, serial, text, varchar, timestamp } from 'drizzle-orm/pg-core';

export const users = pgTable('users', {
  id: serial('id').primaryKey(),
  email: varchar('email', { length: 255 }).notNull().unique(),
  name: text('name').notNull(),
  password: text('password').notNull(),
  createdAt: timestamp('created_at').defaultNow().notNull(),
  updatedAt: timestamp('updated_at').defaultNow().notNull(),
});

export type User = typeof users.$inferSelect;
export type NewUser = typeof users.$inferInsert;

步骤4:创建DrizzleORM模块

接下来,创建一个NestJS模块来处理DrizzleORM的初始化和注入:

ts 复制代码
// src/drizzle/drizzle.module.ts
import { Module, Global } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import { DrizzleService } from './drizzle.service';

@Global()
@Module({
  providers: [
    {
      provide: 'DRIZZLE_ORM',
      inject: [ConfigService],
      useFactory: (configService: ConfigService) => {
        const connectionString = configService.get<string>('DATABASE_URL');
        const client = postgres(connectionString);
        return drizzle(client);
      },
    },
    DrizzleService,
  ],
  exports: [DrizzleService],
})
export class DrizzleModule {}

步骤5:创建DrizzleORM服务

现在创建一个服务来提供对DrizzleORM实例的访问:

ts 复制代码
// src/drizzle/drizzle.service.ts
import { Inject, Injectable } from '@nestjs/common';
import { PostgresJsDatabase } from 'drizzle-orm/postgres-js';

@Injectable()
export class DrizzleService {
  constructor(
    @Inject('DRIZZLE_ORM')
    private readonly db: PostgresJsDatabase,
  ) {}

  getDB() {
    return this.db;
  }
}

步骤6:创建用户服务

接下来,创建一个用户服务来处理用户相关的操作:

ts 复制代码
// src/users/users.service.ts
import { Injectable } from '@nestjs/common';
import { DrizzleService } from '../drizzle/drizzle.service';
import { users, NewUser, User } from '../db/schema/users';
import { eq } from 'drizzle-orm';

@Injectable()
export class UsersService {
  constructor(private drizzleService: DrizzleService) {}

  async findAll(): Promise<User[]> {
    const db = this.drizzleService.getDB();
    return db.select().from(users);
  }

  async findOne(id: number): Promise<User | null> {
    const db = this.drizzleService.getDB();
    const result = await db.select().from(users).where(eq(users.id, id));
    return result[0] || null;
  }

  async create(userData: NewUser): Promise<User> {
    const db = this.drizzleService.getDB();
    const result = await db.insert(users).values(userData).returning();
    return result[0];
  }

  async update(id: number, userData: Partial<NewUser>): Promise<User | null> {
    const db = this.drizzleService.getDB();
    const result = await db
      .update(users)
      .set(userData)
      .where(eq(users.id, id))
      .returning();
    return result[0] || null;
  }

  async remove(id: number): Promise<void> {
    const db = this.drizzleService.getDB();
    await db.delete(users).where(eq(users.id, id));
  }
}

步骤7:创建用户控制器

最后,创建一个控制器来处理HTTP请求:

ts 复制代码
// src/users/users.controller.ts
import {
  Controller,
  Get,
  Post,
  Body,
  Param,
  Put,
  Delete,
} from '@nestjs/common';
import { UsersService } from './users.service';
import { NewUser } from '../db/schema/users';

@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Get()
  findAll() {
    return this.usersService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.usersService.findOne(+id);
  }

  @Post()
  create(@Body() createUserDto: NewUser) {
    return this.usersService.create(createUserDto);
  }

  @Put(':id')
  update(@Param('id') id: string, @Body() updateUserDto: Partial<NewUser>) {
    return this.usersService.update(+id, updateUserDto);
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.usersService.remove(+id);
  }
}

使用DrizzleORM的高级特性

DrizzleORM提供了许多高级特性,可以在NestJS应用程序中充分利用。

关系查询

DrizzleORM允许你轻松地处理表之间的关系。例如,如果我们有一个关联到用户的帖子表:

ts 复制代码
// src/db/schema/posts.ts
import { pgTable, serial, text, integer, timestamp } from 'drizzle-orm/pg-core';
import { users } from './users';
import { relations } from 'drizzle-orm';

export const posts = pgTable('posts', {
  id: serial('id').primaryKey(),
  title: text('title').notNull(),
  content: text('content').notNull(),
  authorId: integer('author_id')
    .references(() => users.id)
    .notNull(),
  createdAt: timestamp('created_at').defaultNow().notNull(),
  updatedAt: timestamp('updated_at').defaultNow().notNull(),
});

export const postsRelations = relations(posts, ({ one }) => ({
  author: one(users, {
    fields: [posts.authorId],
    references: [users.id],
  }),
}));

export const usersRelations = relations(users, ({ many }) => ({
  posts: many(posts),
}));

export type Post = typeof posts.$inferSelect;
export type NewPost = typeof posts.$inferInsert;

事务

DrizzleORM支持事务,对于需要原子操作的场景非常有用:

ts 复制代码
// 在服务中使用事务
async transferFunds(fromId: number, toId: number, amount: number) {
  const db = this.drizzleService.getDB();
  
  return await db.transaction(async (tx) => {
    // 从一个账户扣款
    await tx
      .update(accounts)
      .set({ balance: sql`balance - ${amount}` })
      .where(eq(accounts.id, fromId));
      
    // 向另一个账户存款
    await tx
      .update(accounts)
      .set({ balance: sql`balance + ${amount}` })
      .where(eq(accounts.id, toId));
      
    // 记录交易
    return await tx
      .insert(transactions)
      .values({ fromId, toId, amount })
      .returning();
  });
}

迁移

DrizzleORM与drizzle-kit结合使用,提供了强大的迁移功能:

bash 复制代码
# 生成迁移
npx drizzle-kit generate

# 应用迁移
npx drizzle-kit push

结论

NestJS和DrizzleORM是构建现代后端应用程序的强大组合。NestJS提供了坚实的架构基础和丰富的功能集,而DrizzleORM通过其轻量级设计和类型安全的特性,补充了这一点。

这种组合尤其适合那些重视代码质量、类型安全和开发体验的团队。通过遵循本文中的模式和实践,你可以创建出既可靠又易于维护的应用程序。

无论你是构建微服务、API还是全栈应用,NestJS和DrizzleORM都可以为你的下一个项目提供坚实的基础。

相关推荐
妖孽白YoonA9 小时前
NestJS 系列教程 - 守卫
nestjs
plusone1 天前
【Nest指北系列】Provider
nestjs
猫头嘤3 天前
重生之我在NestJS代码世界搞 ---- “登录鉴权”!
后端·node.js·nestjs
seelingzheng5 天前
Nest系列:从环境变量到工程化实践-2
nestjs
凉凉的知识库7 天前
搞懂常见Go ORM系列-开篇
后端·go·orm
2301_7930698217 天前
Spring Boot +SQL项目优化策略,GraphQL和SQL 区别,Spring JDBC 等原理辨析(万字长文+代码)
java·数据库·spring boot·sql·jdbc·orm
HxY22 天前
Server Sent Event 技术实践
前端·后端·nestjs
三天不学习1 个月前
【并发控制、更新、版本控制】.NET开源ORM框架 SqlSugar 系列
开源·.net·orm·sqlsugar
明月看潮生1 个月前
青少年编程与数学 02-009 Django 5 Web 编程 05课题、数据库与ORM
python·青少年编程·django·orm·编程与数学