使用 NestJS 将你的 Node 后端提升到新的水平

2025-06-04

使用 NestJS 将你的 Node 后端提升到新的水平

作者:Maciej Cieślar ✏️

NestJS 是一个用于创建可扩展应用程序的后端框架。它作为底层 HTTP 服务器库的抽象。目前,它支持 Express 和 Fastify 两个库,同时仍允许开发人员在需要时使用自己的 API。

更重要的是,它通过引入类似 Angular 的模块、服务和控制器强制开发人员使用特定的架构,确保应用程序具有可扩展性、高度可测试性和松散耦合性。

Express、Koa 和 hapi 并不强制任何特定的架构。它们确实提供了一套底层工具,但仍然给开发者留下了很多自主权。项目早期在架构上犯下的错误,可能会在后期重构代码库时耗费大量时间。

相反,Nest 对代码库(其模块、服务和控制器)非常严格,因此你不会出错。

LogRocket 免费试用横幅

建筑学

团队合作开发项目意味着,关于应用程序的结构,存在着许多不同的偏好。将这些偏好强加到一个代码库中,不会带来任何改进,反而会造成混乱,并导致整体代码质量低下。

为了解决这个问题,Nest 定义了一套标准化的指导方针,团队中的每个开发人员都必须遵循一套规范的架构。最终,代码库变得易于维护。

Nest 通过引入一些简单的构建块抽象出了与 Express 或 Fastify 等底层库的所有处理,其中最重要的是模块、服务和控制器。

NestJS 模块结构

模块封装了与给定领域相关的所有逻辑。假设我们需要实现与用户相关的逻辑,我们可以创建一个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 {}
Enter fullscreen mode Exit fullscreen mode

典型的应用程序将具有诸如ApplicationModule(根模块)之类的模块SharedModule(它将进一步封装所有底层可重用的跨应用程序模块),以及像上面提供的模块,它将封装公共 API 的逻辑。

在此处阅读有关NestJS 模块的更多信息。

控制器

控制器是一个带有@Controller装饰器注解的类。控制器充当传入 HTTP 请求和相应处理逻辑之间的一层。我们可以将控制器视为中间人:它检查传入的请求并调用相应服务的方法。

NestJS 控制器说明

控制器定义路由路径、参数、响应以及与 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`;
  }
}
Enter fullscreen mode Exit fullscreen mode

在上面的示例中,有一个控制器正在监听路径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;
  }
}
Enter fullscreen mode Exit fullscreen mode

通过使用@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();
  }
}
Enter fullscreen mode Exit fullscreen mode

CatsService我们不是提供依赖于with的每个类new CatsService(...deps),而是告诉 Nest,“如果有人在他们的依赖项中请求此类的实例,就为他们创建它。”

默认情况下,提供的实例是单例,并在每个请求实体之间共享,但可以通过指定注入范围来更改。我们的依赖项通常在构造函数中使用private readonly name: type语法定义。

在Angular 的依赖注入指南中阅读有关依赖注入的更多信息

文件结构

强烈建议将代码库的结构反映在文件夹结构中。每个模块都应该有自己独立的文件夹,用于存放其所有内部构建块。

NestJS 文件结构示例

Nest 遵循 Angular 的步骤,也有一个命名约定,包含适当的文件后缀,例如.controller.service.module。这样,项目的文件夹结构易于阅读,并为开发人员提供了良好的代码库概览。

命令行界面

Nest 带有自己的 CLI。

npm i -g @nestjs/cli
Enter fullscreen mode Exit fullscreen mode

最重要的命令是newgenerate

new命令让我们能够在几秒钟内使用 Nest 创建整个启动应用程序的样板。

nest new application-name
Enter fullscreen mode Exit fullscreen mode

generate命令会为所请求的功能生成一组文件。如有必要,它还可能会修改现有的文件。

nest generate service cat
Enter fullscreen mode Exit fullscreen mode

在上面的例子中,将生成两个文件:cat.service.tscat.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);
    });
  });
});
Enter fullscreen mode Exit fullscreen mode

正如我们之前提到的,Nest 的 CLI 为每个服务、控制器、过滤器等生成基本测试。通过这样做,Nest 不仅为我们提供了必要的工具,还提醒我们应该测试应用程序的每个功能。

如果您想了解一些更复杂的情况(即模拟数据库连接),我已经创建了一个应用程序,您可以在此处看到。

概括

Nest 紧跟新趋势,使基于微服务架构编写应用程序变得非常容易。

虽然 Nest 默认用于构建 REST API,但由于其平台无关性,该架构也可用于创建 GraphQL API。在编写 REST API 文档时,它与Swagger库的良好集成使我们能够轻松生成文档。

Nest 生态系统中还有许多软件包可以将现有的软件包集成到 Nest 架构中。最受欢迎的软件包包括typeormpassportmongoose。如果某个库不受支持,我们可以创建自己的服务/提供程序,以 Nest 可访问的方式封装该库。

当然,Nest 并非万能的解决方案,在某些情况下确实存在不足,或者实现预期结果所需的步骤不明确。但随着其社区的不断壮大(GitHub 上超过 2 万颗星),快速获得紧迫问题的答案变得越来越容易。


编者注:觉得这篇文章有什么问题?您可以在这里找到正确版本

插件:LogRocket,一个用于 Web 应用的 DVR

 
LogRocket 仪表板免费试用横幅
 
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
PREV
SCSS 权威指南
NEXT
React Router hooks 将使你的组件更简洁