返回文章列表

1 起步

这篇文章基于 nest.js 10.0 版本,简单介绍了如何从零开始创建一个 Nest 项目,以及 Nest 常见的概念,
ControllerServiceModuleInterceptorPipeGuardMiddleware 等等。

npm i -g @nestjs/cli

nest new project-name

//生成controller
nest g controller name

//生成service
nest g service name

//生成模块
nest g module name

//生成crud
nest g resource name

Controller

Controller 用于处理 HTTP 路由,复杂逻辑则传递给 Service

import { Controller, Get, Req } from "@nestjs/common";
import { Request } from "express";

@Controller("cat") // prefix
export class CatController {
  @Get() // path
  // 参数可以拿到express的Request对象
  findAll(@Req() request: Request): string {
    // 函数名、没意义
    return "This action returns all cats"; //标准模式下:基本类型返回值、对象类型自动序列化成 json
  }
}

Service

Service 负责业务模块的应用逻辑。

Controller 的构造函数里可以注入 Service,注入后即可通过 this.catsService 调用方法。

@Controller("cats")
export class CatsController {
  //注入service
  constructor(private catsService: CatsService) {}
}

尝试使用 Fastify 和版本控制

import { NestFactory } from "@nestjs/core";
import { VersioningType } from "@nestjs/common";
import { AppModule } from "./app.module";
import {
  FastifyAdapter,
  NestFastifyApplication,
} from "@nestjs/platform-fastify";

async function bootstrap() {
  const app = await NestFactory.create<NestFastifyApplication>(
    AppModule,
    new FastifyAdapter()
  );

  app.enableVersioning({
    type: VersioningType.URI,
  });

  await app.listen(3000);
}
bootstrap();

为某个 API 添加版本控制

通过 @Version 装饰器为 API 添加版本号。

@Version('1')
  @Get()
  async findAll(@Query() query: ListAllEntities) {
    return this.catService.findAll(query);
  }

我当时踩了一个坑:添加完版本号后访问 http://localhost:3000/V1/cat 返回 404,是我疏忽了——路由区分大小写。

为某个 Controller 添加版本控制

@Controller({
  path: "cat",
  version: "1",
})
export class CatController {
  //some code
}

为整个 App 添加版本控制

app.enableVersioning({
  type: VersioningType.URI,
  defaultVersion: "1",
});

添加全局中间件

为 API 返回结果提供统一格式。

忽略掉复杂的类型标注,其实这个 class 本质上就是一个简单的拦截器。

next.handle().pipe() 暂时不理解也可以先忽略。

// common/interceptors/transform.interceptor.ts
// 全局中间件

import {
  CallHandler,
  NestInterceptor,
  ExecutionContext,
  Injectable,
} from "@nestjs/common";
import { Observable } from "rxjs";
import { map } from "rxjs";

@Injectable()
export class TransformInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next.handle().pipe(map((data) => ({ data, message: "success" })));
  }
}

统一返回格式后,再基于拦截器为 App 添加全局异常拦截。

// common/exceptions/base.exception.filter.ts
// 全局异常拦截
// common/exceptions/http.exception.filter.ts
// 处理HTTP异常

基于 VS Code 调试

点击 VS Code 的 Debug 面板,创建配置文件。
删除默认的配置项,点击右下角 Add Configuration,输入 via npm,选择备选项,即可添加如下配置。

via npm 表示通过 npm 启动调试。

start:debugpackage.json 中定义的脚本。

runtimeVersion 是 Node.js 的版本,VS Code 会基于 nvm 切换 Node 版本。

internalConsoleOptions 是 VS Code 的配置,neverOpen 表示不打开内部控制台。

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Launch via NPM",
      "request": "launch",
      "runtimeArgs": ["run-script", "start:debug"],
      "runtimeExecutable": "npm",
      "runtimeVersion": "20.17.0",
      "internalConsoleOptions": "neverOpen",
      "skipFiles": ["<node_internals>/**"],
      "type": "node"
    }
  ]
}

创建好配置文件后,无需再使用命令行启动调试,直接点击 VS Code 的 Debug 按钮即可。

基于 Chrome 调试

有时需要基于线上环境调试线上问题,可以使用 Chrome 调试。

Nest 提供了 start:debug 脚本,本质是开启 inspect 模式

随后我们可以通过浏览器访问 chrome://inspect,发现 Remote Target 中有一个 Nest 的 App,点击 inspect,即可调起 DevTools 进行调试。

开始时的一些坑

  • 路由区分大小写
  • 直接返回字符串导致前端报错(无法解析 JSON)
  • Controller 里的函数忘记 return,导致前端收到的响应是空白的
  • 在 Service 函数里试图在 setTimeout 里返回响应来模拟延迟