1 起步
这篇文章基于 nest.js 10.0 版本,简单介绍了如何从零开始创建一个 Nest 项目,以及 Nest 常见的概念,
如 Controller、Service、Module、Interceptor、Pipe、Guard、Middleware 等等。
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:debug 是 package.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里返回响应来模拟延迟