使用 NestJS 将你的 Node 后端提升到新的水平
作者:Maciej Cieślar ✏️
NestJS 是一个用于创建可扩展应用程序的后端框架。它作为底层 HTTP 服务器库的抽象。目前,它支持 Express 和 Fastify 两个库,同时仍允许开发人员在需要时使用自己的 API。
更重要的是,它通过引入类似 Angular 的模块、服务和控制器强制开发人员使用特定的架构,确保应用程序具有可扩展性、高度可测试性和松散耦合性。
Express、Koa 和 hapi 并不强制任何特定的架构。它们确实提供了一套底层工具,但仍然给开发者留下了很多自主权。项目早期在架构上犯下的错误,可能会在后期重构代码库时耗费大量时间。
相反,Nest 对代码库(其模块、服务和控制器)非常严格,因此你不会出错。
建筑学
团队合作开发项目意味着,关于应用程序的结构,存在着许多不同的偏好。将这些偏好强加到一个代码库中,不会带来任何改进,反而会造成混乱,并导致整体代码质量低下。
为了解决这个问题,Nest 定义了一套标准化的指导方针,团队中的每个开发人员都必须遵循一套规范的架构。最终,代码库变得易于维护。
Nest 通过引入一些简单的构建块抽象出了与 Express 或 Fastify 等底层库的所有处理,其中最重要的是模块、服务和控制器。
模块封装了与给定领域相关的所有逻辑。假设我们需要实现与用户相关的逻辑,我们可以创建一个UserModule
包含UserService
、的模块UserController
。
模块本身只是一个用@Module
装饰器修饰的类,我们在其中提供了所有必要的元数据。如果我们想从其他模块访问模块的某个部分,我们可以将该部分导出到exports
数组中。
以下是CatModule
官方文档的内容:
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
@Module({
controllers: [CatsController],
providers: [CatsService],
exports: [CatsService]
})
export class CatsModule {}
典型的应用程序将具有诸如ApplicationModule
(根模块)之类的模块SharedModule
(它将进一步封装所有底层可重用的跨应用程序模块),以及像上面提供的模块,它将封装公共 API 的逻辑。
在此处阅读有关NestJS 模块的更多信息。
控制器
控制器是一个带有@Controller
装饰器注解的类。控制器充当传入 HTTP 请求和相应处理逻辑之间的一层。我们可以将控制器视为中间人:它检查传入的请求并调用相应服务的方法。
控制器定义路由路径、参数、响应以及与 HTTP 请求相关的所有其他内容。它们不关心请求和响应之间发生了什么。
@Controller('cats')
export class CatsController {
@Post()
create(@Body() createCatDto: CreateCatDto) {
return 'This action adds a new cat';
}
@Get()
findAll(@Query() query: ListAllEntities) {
return `This action returns all cats (limit: ${query.limit} items)`;
}
@Get(':id')
findOne(@Param('id') id: string) {
return `This action returns a #${id} cat`;
}
@Put(':id')
update(@Param('id') id: string, @Body() updateCatDto: UpdateCatDto) {
return `This action updates a #${id} cat`;
}
@Delete(':id')
remove(@Param('id') id: string) {
return `This action removes a #${id} cat`;
}
}
在上面的示例中,有一个控制器正在监听路径application_url/cats
。每个方法都用@Get
、@Post
、@Put
或修饰@Delete
,以指示它们感兴趣的 HTTP 方法。我们还可以在装饰器内部提供额外的路径。
我们不需要直接使用请求对象来访问正文或查询(就像在 Express 中一样),而是可以用@Param
、@Query
或来修饰每个参数@Body
,从而可以抽象底层访问层。
尽管上面示例中的每个方法都是同步工作的,但方法可能会返回带有承诺和可观察对象的异步值。
服务
服务是一个使用@Injectable
装饰器注解的类。它包含领域(业务)逻辑。通过分离访问层(控制器)和逻辑层(服务),我们可以实现清晰的关注点分离。
@Injectable()
export class CatsService {
private readonly cats: Cat[] = [];
create(cat: Cat) {
this.cats.push(cat);
}
findAll(): Cat[] {
return this.cats;
}
}
通过使用@Injectable
装饰器,我们告诉 Nest 这个类可以注入到应用程序的其他部分。
阅读有关服务的更多信息。
依赖注入
依赖注入是 Nest 最重要的功能之一。通过提供开箱即用的支持,Nest 使我们能够编写松耦合的代码,从而易于测试。
由于 Nest 是用 TypeScript 编写的,因此依赖关系只需按类型即可解决 - 无需手动注入它们!
@Controller('cats')
export class CatsController {
constructor(private readonly catsService: CatsService) {}
@Post()
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
@Get()
async findAll(): Promise<Cat[]> {
return this.catsService.findAll();
}
}
CatsService
我们不是提供依赖于with的每个类new CatsService(...deps)
,而是告诉 Nest,“如果有人在他们的依赖项中请求此类的实例,就为他们创建它。”
默认情况下,提供的实例是单例,并在每个请求实体之间共享,但可以通过指定注入范围来更改。我们的依赖项通常在构造函数中使用private readonly name: type
语法定义。
在Angular 的依赖注入指南中阅读有关依赖注入的更多信息。
文件结构
强烈建议将代码库的结构反映在文件夹结构中。每个模块都应该有自己独立的文件夹,用于存放其所有内部构建块。
Nest 遵循 Angular 的步骤,也有一个命名约定,包含适当的文件后缀,例如.controller
、.service
和.module
。这样,项目的文件夹结构易于阅读,并为开发人员提供了良好的代码库概览。
命令行界面
Nest 带有自己的 CLI。
npm i -g @nestjs/cli
最重要的命令是new
和generate
。
该new
命令让我们能够在几秒钟内使用 Nest 创建整个启动应用程序的样板。
nest new application-name
该generate
命令会为所请求的功能生成一组文件。如有必要,它还可能会修改现有的文件。
nest generate service cat
在上面的例子中,将生成两个文件:cat.service.ts
和cat.service.spec.ts
。此外,如果cat
目录中指定了模块,则服务将自动导入。
CLI 通过减少每个构建块需要编写的样板量并生成.spec
包含一些非常基本的测试的文件,真正提高了开发人员的工作效率。
测试
测试对于确保应用程序在进行某些更改后仍能正常运行至关重要。Nest 非常重视测试,并提供测试实用程序,以使测试过程尽可能顺畅。借助 Nest 的自定义提供程序功能,通过使用依赖注入,我们可以非常轻松地模拟当前未测试的模块。
以下是单元测试的一个例子CatsController
:
import { Test } from '@nestjs/testing';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
describe('CatsController', () => {
let catsController: CatsController;
let catsService: CatsService;
beforeEach(async () => {
const module = await Test.createTestingModule({
controllers: [CatsController],
providers: [CatsService],
}).compile();
catsService = module.get<CatsService>(CatsService);
catsController = module.get<CatsController>(CatsController);
});
describe('findAll', () => {
it('should return an array of cats', async () => {
const result = ['test'];
jest.spyOn(catsService, 'findAll').mockImplementation(() => result);
expect(await catsController.findAll()).toBe(result);
});
});
});
正如我们之前提到的,Nest 的 CLI 为每个服务、控制器、过滤器等生成基本测试。通过这样做,Nest 不仅为我们提供了必要的工具,还提醒我们应该测试应用程序的每个功能。
如果您想了解一些更复杂的情况(即模拟数据库连接),我已经创建了一个应用程序,您可以在此处看到。
概括
Nest 紧跟新趋势,使基于微服务架构编写应用程序变得非常容易。
虽然 Nest 默认用于构建 REST API,但由于其平台无关性,该架构也可用于创建 GraphQL API。在编写 REST API 文档时,它与Swagger库的良好集成使我们能够轻松生成文档。
Nest 生态系统中还有许多软件包可以将现有的软件包集成到 Nest 架构中。最受欢迎的软件包包括typeorm、passport和mongoose。如果某个库不受支持,我们可以创建自己的服务/提供程序,以 Nest 可访问的方式封装该库。
当然,Nest 并非万能的解决方案,在某些情况下确实存在不足,或者实现预期结果所需的步骤不明确。但随着其社区的不断壮大(GitHub 上超过 2 万颗星),快速获得紧迫问题的答案变得越来越容易。
编者注:觉得这篇文章有什么问题?您可以在这里找到正确版本。
插件:LogRocket,一个用于 Web 应用的 DVR
LogRocket是一款前端日志工具,可让您重播问题,就像它们发生在您自己的浏览器中一样。无需猜测错误发生的原因,也无需要求用户提供屏幕截图和日志转储,LogRocket 让您重播会话,快速了解问题所在。它可与任何应用程序完美兼容,不受框架限制,并且提供插件来记录来自 Redux、Vuex 和 @ngrx/store 的额外上下文。
除了记录 Redux 操作和状态外,LogRocket 还记录控制台日志、JavaScript 错误、堆栈跟踪、带有标头 + 正文的网络请求/响应、浏览器元数据以及自定义日志。它还会对 DOM 进行插桩,以记录页面上的 HTML 和 CSS,即使是最复杂的单页应用程序,也能重现像素完美的视频。
免费试用。
文章“使用 NestJS 将您的 Node 后端提升到新的水平”最先出现在LogRocket 博客上。
文章来源:https://dev.to/bnevilleoneill/take-your-node-back-end-to-the-next-level-with-nestjs-2fj7