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都可以为你的下一个项目提供坚实的基础。