适用于 node 接口层文档,包含文件、规范、开发示例等
$ npm install
依赖模块
- @nestjs/common
- @nestjs/core
- @nestjs/...
- winston
- dayjs
- pm2
- ...
# develop
$ npm run dev
# build app
$ npm run build
# build docker image
$ npm run build:docker
# build proto buffer
$ npm run build:protocol
# production with docker
$ npm run startup:docker
# production with pm2
$ npm run startup
# unit tests
$ npm run test
# e2e tests
$ npm run test:e2e
# test coverage
$ npm run test:cov
存放于业务模块,文件按业务拆分,该项目文件未按照角色拆分,而是按照业务模块拆分。即同模块下,controller、service、module、dto、dto、static 存放于一个文件夹下
文件名 | 路径 | 功能描述 |
---|---|---|
*.controller.ts | src/app/*/*.controller.ts | 控制器,负责数据校验,数据组装,请注意,controller 不负责重逻辑,重逻辑请放置于 service!!! |
*.service.ts | src/app/*/*.service.ts | 业务处理,例如发起 gRPC 请求 |
*.do.ts | src/app/*/*.do.ts | 向服务端(数据库)接收、传输的数据实体 |
*.dto.ts | src/app/*/*.dto.ts | 向前端接收、传输的数据实体,目前未严格区分 do、dto,如何使用请自行决定 |
*.static.ts | src/app/*/*.static.ts | 模块的枚举、interface、type、常量等定义 |
提示:如果业务模块过于复杂,可以再进行拆分,一切以好维护为标准,例如可以按照如下进行拆分:
- test
- test1
- test1.controller.ts
- test1.service.ts
...
- test2
- test1.controller.ts
- test1.service.ts
...
test.module.ts
存放公用包,如 nest 定义的不同角色的模块
文件名 | 路径 | 功能描述 |
---|---|---|
transform.decorator.ts | src/common/decorator/transform.decorator.ts | 数据实体转换、类型校验等装饰器文件 |
user.decorator.ts | src/common/decorator/user.decorator.ts | 用户信息设置、获取、权限设置等装饰器文件 |
文件名 | 路径 | 功能描述 |
---|---|---|
all.exception.filter.ts | src/common/filter/all.exception.filter.ts | 所有异常捕获,组装错误码、错误信息并返回 |
文件名 | 路径 | 功能描述 |
---|---|---|
auth.guards.ts | src/common/guards/auth.guards.ts | auth.guards.ts (免登录使用 AuthIgnore 装饰器) |
role.guards.ts | src/common/guards/role.guards.ts | 角色权限守卫(配合 Roles、UseGuards 装饰器使用) |
文件名 | 路径 | 功能描述 |
---|---|---|
response.interceptor.ts | src/common/interceptor/response.interceptor.ts | 返回值统一格式序列化 |
文件名 | 路径 | 功能描述 |
---|---|---|
log.middware.ts | src/common/middware/log.middware.ts | 服务入口通用日志打印 |
文件名 | 路径 | 功能描述 |
---|---|---|
log.service.ts | src/common/module/log.service.ts | 日志服务 |
config.service.ts | src/common/module/config.service.ts | 服务配置 |
文件名 | 路径 | 功能描述 |
---|---|---|
index.ts | src/config/index.ts | 配置文件 |
src/config.ts 只放置通用配置,对不同环境配置或敏感信息配置不要放在此处!!!,请在配置工程目录中配置环境变量!!!
文件名 | 路径 | 功能描述 |
---|---|---|
helper.ts | src/helper/helper.ts | 工具函数 |
error.ts | src/helper/error.ts | 错误码、错误信息映射 |
增加工具函数,请增加测试,工具函数测试覆盖率必须 100%!!!
启动前执行脚本,为在启动服务前需要执行的脚本,例如本项目的 require hack
- 加载 boot.ts
- 执行 bootstrap
- HTTP 启动时,端口占用处理
- 错误捕获
命名 | 规则 | 示例 |
---|---|---|
文件夹命名 | 文件夹命名统一小写,如果存在多单词,则使用短横线连接 | user、user-detail |
文件命名 | 文件名统一小写,且按照角色命名,如果存在多单词,则使用 "." 连接 | user.controller.ts、user.detail.controller.ts |
class 命名 | 采用大驼峰结构 | Class UserController {} |
方法命名 | 采用小驼峰结构 | indexUser(){} |
enum | 必须以大写字母"E"起始;枚举值必须采用大写字母,或大写字符加下划线 | enum EUser { SEX = 0, USER_SEX = 1}; |
interface | 以大写字母"I"起始的大驼峰结构 | interface IUser {} |
type | 以大写字母"T"起始的大驼峰结构 | type TUser {} |
常量 | 以小写字母"k"起始、全大写字母、大写字母加下划线 | const kUserName; const USER_NAME; |
变量 | 变量采用小驼峰结构 | const userName |
- controller 必须使用 ApiUseTags 注释,注释规则:
@ApiUseTags('user: 用户模块')
- controller import 语句放置位置
- 顶层放置: Node Native Module 或 三方包
- 中间放置: 自己的文件模块
- 底层放置: 对 protocol 文件的引用
如下所示
import { ... } from 'fs';
import { ... } from '@nestjs/common'
import { UserService } from '@App/user/user.service';
import { user } from '@Protocol/user.d.ts';
- controller 内方法命名
方法 | 规则 | 示例 |
---|---|---|
GET / | 列表查询:以 index 起始 | indexUser(){} |
GET /:id | 检索查询:以 show 起始 | showUser(){} |
GET / | 导出:以 export 起始 | exportUser(){} |
POST / | 新增:以 create 起始 | createUser(){} |
PUT /:id | 编辑:以 update 起始 | updateUser(){} |
DELETE /:id | 删除:以 delete 起始 | deleteUser(){} |
测试方法 | 测试:以 test 起始 | testUser(){} |
其余特殊方法可单独命名
- controller method 必须定义 swagger 注释
@ApiOperation()
- controller method 如果返货为 JSON 格式的标准输出数据,则需要以
@ResponseSerialize(XX)
转化,否则会以原有格式返回
- service 命名方式可选择和 controller 保持一致
- service 调用日志: service 可使用日志装饰器
@Log()
,该装饰器会打印 service 进出日志,如果开启 DEBUG(如何开启 DEBUG 日志),则会打印 service 的完整输出,使用该装饰器时,service 必须接受第一个参数为 Request 实体,该实体由 controller 传输,如下所示。
传输 Request 实体原因:
- 调用日志打印必须存在 requestId,而 service 无法获取 req
- 日志打印时,会开启 DEBUG 判断,该参数从 query 传递,service 无法获取 query
// ---- user.controller.ts ----
...
@Get('/')
testUser(@Req() req: IRequest): string {
return this.userService.test(req, true);
}
...
// ---- user.service.ts ----
...
@Log()
test(req: IRequest, query: boolean): string {
if (query) {
return 'hello,nest';
}
return 'hello,word'
}
...
- dto 属性必须存在 swagger 注释
@ApiProperty()
或ApiPropertyOptional()
(注意 7.x 和 6.x 的方法名); 请注意 required 属性,如果前端 *.d.ts 是根据此 swagger 文档生成,会对前端传参造成影响 - 是否需要 *.do.ts 开发者自己决定
- 如果 dto 太多,则需要拆分文件,按照 src/app 示例 拆分
- 日志采用 winston,由 log.service.ts 提供服务
- 日志按天切割,例如 2020-3-31.log、2020-4-01.log
单条日志信息格式为:
2020-03-32 00:00:00 WARN 调用xx服务错误
2020-03-32 00:00:00 INFO 进入系统
2020-03-32 00:00:00 DEBUG 调用xx服务
import { LogService } from '@Log/log.service';
LogService.info(xxx);
- 系统日志: 描述系统状态,如磁盘使用、内存使用、CPU 使用等。由运维人员维护
- 诊断日志:
- 系统启动日志: 由模板完成,开发人员无需关心
- 系统配置日志: 由模板完成,开发人员无需关心
- 操作日志、调用日志: 由开发人员自行打印
- 统计日志: 统一用户使用情况、用户操作等。根据业务而定
- FATAL: 致命错误,服务已经挂了,需要运维立处理,无需开发人员打印
- ERROR: 严重错误,服务已经不可访问,需要运维立处理,无需开发人员打印
- WARN: 警告错误,服务科正常访问,但可能会导致问题,不需要立即处理
- INFO: 消息日志,记录运行状态
- DEBUG: 消息日志,记录详细操作步骤
- 系统启动日志: 由 main.ts 负责打印
- 系统配置日志: 由 config.service.ts、boot.ts 等负责打印
- 进入系统日志: 由 log.middware.ts 负责打印
- 系统调用日志: 由各个 *.service.ts 负责打印
- 离开系统体质: 由 response.interceptor.ts 负责打印
- 异常日志: 由 all.exception.filter.ts 负责打印
线上日志默认关闭 DEBUG,只会打印服务的调用流程,而不会打印完整的信息,可以在 url 参数中加入DEBUG=1 开启 DEBUG 模式。tips: 如果使用 LogService.debug()
时,请注意调用参数
requestId 做为用户调用的唯一标识,标识用户在整个系统中调用身份,由 log.middware.ts 维护。打印调用日志时,请务必打印 requestId,否则将无法知晓用户在系统中的调用过程
日志统一使用 LogService 打印,线上代码不允许出现 console.log !!! 日志
- 配置采用 dotenv 包,由 config.service.ts 提供服务
- 配置先读取 src/config.ts,再读取根目录 .env,且如果 key 值相同,则使用 .env 覆盖 src/config.ts
import { ConfigService } from '@Config/config.service';
ConfigService.get(xxx);
- 在 src/app 下创建模块,例如 user 模块,则创建 user.module.ts、user.controller.ts...
- 将 user.controller.ts 和 user.service.ts 加入 user.module.ts 中
将创建好的user.module.ts 加入 app.module.ts中,此时即可使用了
- 未做接口防刷
- 未做 csrf 防御
- 未做 XSS 防御,由于未涉及数据库,也不涉及单独返回数据(该项目请求微服务,将微服务返回的数据做转发),所以对输入输出都未做任何处理