
在Nestjs 入门(二)中,我们创建了一个基本的 Nestjs 应用。下面我们基于此进行扩展。
源码地址:awesome-nest
序列化
在 entity 中,有时候有些字段不一定要返还给前端,通常我们需要自己做一次筛选,而 Nestjs 中,配合 class-transformer,可以很方便的实现这个功能。
例如,我们有个 entity 的基类common.entity.ts,返还数据的时候,我们不希望把create_at和update_at也带上,这时候就可以使用@Exclude()排除CommonEntity中的这两个字段:
1 | import { CreateDateColumn, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm' |
在对应请求的地方标记使用ClassSerializerInterceptor,此时,GET /api/v1/cats/1这个请求返回的数据中,就不会包含create_at和update_at这两个字段。
1 | ('cats') |
如果某个 controller 中都需要使用ClassSerializerInterceptor来帮我们做一些序列化的工作,可以把 Interceptor 提升到整个 controller:
1 | (ClassSerializerInterceptor) |
甚至可以在main.ts中把它作为全局的 Interceptor,不过这样不方便进行细粒度地控制。
1 | async function bootstrap() { |
在某些场景下,我们需要对 entity 中某个字段处理后再返回,可以使用@Transform():
1 | ('dog') |
此时,name字段经过@Transform的包装就会变成dog: name的格式。如果我们需要根据已有字段构造一个新的字段,可以使用@Expose():
1 | ('dog') |
上面代码会根据查询到的age字段动态计算isOld的值,此时通过 GET 方法请求返回的结果如下:
1 | { |
事务
在使用 MySQL 的时候,有时候我们需要使用事务,借助 TypeORM 中可以这样使用事务:
1 | (':name') |
@Transaction()将 controller 或者 service 中所有执行包装到一个数据库事务中,@TransportManager提供了一个事务实体管理器,它必须用于在该事务中执行查询:
1 | async deleteCat(name: string, manager: EntityManager): Promise<void> { |
上面代码通过装饰器很方便地进行了事务的操作,如果事务执行过程中有任何错误会自动回滚。
当然,我们也可以手动创建查询运行程序实例,并使用它来手动控制事务状态:
1 | import { getConnection } from "typeorm"; |
QueryRunner提供单个数据库连接。 使用查询运行程序组织事务。 单个事务只能在单个查询运行器上建立。
认证
在这个应用内,现在对用户还没有进行认证,通过用户认证可以判断该访问角色的合法性和权限。通常认证要么基于 Session,要么基于 Token。这里就以基于 Token 的 JWT(JSON Web Token) 方式进行用户认证。
首先安装相关依赖:
1 | $ npm install --save @nestjs/passport passport @nestjs/jwt passport-jwt |
然后创建jwt.strategy.ts,用来验证 token,当 token 有效时,允许进一步处理请求,否则返回401(Unanthorized):
1 | import { ExtractJwt, Strategy } from 'passport-jwt' |
然后创建auth.service.ts,上面的jwt.strategy.ts会使用这个服务校验 token,并且提供了创建 token 的方法:
1 | import { JwtService } from '@nestjs/jwt' |
这两个文件都会作为服务在对应的module中注册,并且引入PassportModule和JwtModule:
1 | import { Module } from '@nestjs/common' |
这时候,就可以使用@UseGuards(AuthGuard())来对需要认证的 API 进行身份校验:
1 | import { |
通过 Postman 模拟请求时,如果没有带上 token,就会返回下面结果:
1 | { |
安全
Web 安全中,常见有两种攻击方式:XSS(跨站脚本攻击) 和 CSRF(跨站点请求伪造)。
对 JWT 的认证方式,因为没有 cookie,所以也就不存在 CSRF。如果你不是用的 JWT 认证方式,可以使用csurf这个库去解决这个安全问题。
对于 XSS,可以使用helmet去做安全防范。helmet 中有 12 个中间件,它们会设置一些安全相关的 HTTP 头。比如xssFilter就是用来做一些 XSS 相关的保护。
对于单 IP 大量请求的暴力攻击,可以用express-rate-limit来进行限速。
对于常见的跨域问题,Nestjs 提供了两种方式解决,一种通过app.enableCors()的方式启用跨域,另一种像下面一样,在 Nest 选项对象中启用。
最后,所有这些设置都是作为全局的中间件启用,最后main.ts中,和安全相关的设置如下:
1 | import * as helmet from 'helmet' |
HTTP 请求
Nestjs 中对Axios进行了封装,并把它作为 HttpService 内置到HttpModule中。HttpService返回的类型和 Angular 的 HttpClient Module一样,都是observables,所以可以使用 rxjs 中的操作符处理各种异步操作。
首先,我们需要导入HttpModule:
1 | import { Global, HttpModule, Module } from '@nestjs/common' |
这里我们把 HttpModule作为全局模块,在sharedModule中导入并导出以便其他模块使用。这时候我们就可以使用HttpService,比如我们在LunarCalendarService中注入HttpService,然后调用其 get方法请求当日的农历信息。这时候get返回的是 Observable。
对于这个 Observable流,可以通过pipe进行一系列的操作,比如我们直接可以使用 rxjs 的map操作符帮助我们对数据进行一层筛选,并且超过 5s 后就会报 timeout 错误,catchError会帮我们捕获所有的错误,返回的值通过of操作符转换为observable:
1 | import { HttpService, Injectable } from '@nestjs/common' |
如果需要对axios 进行配置,可以直接在 Module 注册的时候设置:
1 | import { Global, HttpModule, Module } from '@nestjs/common' |
模板渲染
在 Nestjs 中,可以使用 hbs 作为模板渲染引擎:
1 | $ npm install --save hbs |
在main.ts中,我们告诉 express,static文件夹用来存储静态文件,views中含了模板文件:
1 | import { NestFactory } from '@nestjs/core' |
在views下新建一个catsPage.hbs的文件,假设,我们需要在里面填充的数据结构是这样:
1 | { |
此时,可以这样写模板:
1 |
|
需要注意的是,如果你有拦截器,数据会先经过拦截器的处理,然后再填充到模板中。
在 controller 中,通过@Render指定模板的名称,并且在 return 中返回需要填充的数据:
1 | ('page') |
Nestjs 还支持和其他 SSR 框架集成,比如 Next,Angular Universal,Nuxt。具体使用 Demo 可以分别查看这几个项目nestify,nest-angular,simple-todos。
Swagger 文档
Nestjs 中也提供了对 swagger 文档的支持,方便我们对 API 进行追踪和测试:
1 | $ npm install --save @nestjs/swagger swagger-ui-express |
在main.ts中构件文档:
1 | const options = new DocumentBuilder() |
此时,访问http://localhost:3300/docs就可以看到 swagger 文档的页面。
对于不同的 API 可以在 controller 中使用@ApiUseTags()进行分类,对于需要认证的 API,可以加上@ApiBearerAuth(),这样在 swagger 中填完 token 后,就可以直接测试 API:
1 | ('cats') |

对于我们定于的 DTO,为了使 SwaggerModule 可以访问类属性,我们必须用 @ApiModelProperty() 装饰器标记所有这些属性:
1 | import { ApiModelProperty } from '@nestjs/swagger' |

对于 swagger 文档更多的用法,可以看官网OpenAPI (Swagger)的内容。
热重载
在开发的时候,运行npm run start:dev的时候,是进行全量编译,如果项目比较大,全量编译耗时会比较长,这时候我们可以利用 webpack 来帮我们做增量编译,这样会大大增加开发效率。
首先,安装 webpack 相关依赖:
1 | $ npm i --save-dev webpack webpack-cli webpack-node-externals ts-loader |
在根目录下创建一个webpack.config.js:
1 | const webpack = require('webpack'); |
在main.ts中启用 HMR:
1 | declare const module: any; |
在package.json中增加下面两个命令:
1 | { |
运行npm run webpack之后,webpack 开始监视文件,然后在另一个命令行窗口中运行npm start。