Skip to content
使用文档

后端指南 ​


在阅读本指南前,我们假设您已经阅读过Nest.js官方文档并能够独立本机启动MySQL与Redis的能力。

项目初始化 ​

快速开始

后端启动 ​

开发阶段通常不会使用docker进行启动,更多的是本地启动。首先我们要配置环境变量文件, 也就是.env文件.

配置项类型描述
DATABASE_HOSTstring数据库IP
DATABASE_PORTstring数据库端口
DATABASE_USERNAMEstring数据库用户名
DATABASE_PASSWORDstring数据库密码
DATABASE_NAMEstring数据库名
DATABASE_SYNCHRONIZEboolean是否自动同步
这很危险, 如果设置为true, 请确保你的DATABASE_HOST是本地环境!
DATABASE_AUTOLOADENTITIESboolean是否自动加载Entry (建议设置为true)
AUTH_SECRETstringJWT secrect
REDIS_SECONDSnumberAccessToken过期时间
REDIS_HOSTstringRedis IP
REDIS_PORTstring数据库 端口
EXPIRES_INstringJwT过期时间 (已废弃)
PAGINATION_PAGEnumber默认页码
PAGINATION_LIMITnumber默认页大小
GLOBAL_PREFIXstringapi全局前缀
MOCK_REGEXstringmock接口glob表达式
REFRESH_TOKEN_TTLnumber刷新令牌过期时间
DEVICE_LIMITnumber设备数量限制, -1表示无限制
PREVIEW_MODEboolean是否启用演示模式, 如果设置为true, 则会拒绝所有的增加、修改、删除操作
ENABLE_SWAGGERboolean是否启用SWAGGER
SWAGGER_TITLEstringSwagger文档标题
SWAGGER_DESCstringSwagger文档简介
SWAGGER_VERSIONstringSwagger文档版本

开发前检查清单 ​

  • [ ] 后端项目已被初始化
  • [ ] .env文件中DATABASE_HOST是开发环境
  • [ ] .env文件中DATABASE_NAME为开发库
  • [ ] .env文件中DATABASE_NAME存在
  • [ ] .env文件中DATABASE_SYNCHRONIZE为true
  • [ ] .env文件中REDIS_HOST是开发环境
  • [ ] MySQL服务可以正常访问
  • [ ] Redis服务可以正常访问
  • [ ] dist目录被删除 (可选,如果你不需要测试初始化数据的话)

配置好文件后您可以运行npm run start:dev来运行后端服务。当出现下述字样时,表示后端启动成功。

LOG [NestApplication] Nest application successfully started +11ms
Application is running on: http://[::1]:3000

生成迁移文件 ​

有时,我们需要改动数据库结构。在修改完成后必须执行pnpm run migrate:gen来生成迁移文件。在运行该命令期间,请确保开发环境数据库可以访问。

修改表结构 ​

假设我们修改了 User 表. 它位于 nestJs/libs/models/src 下.

diff
export class User {
  @PrimaryGeneratedColumn()
  id: number;
  @Column()
  name: string;
  @Column()
+ nickName: string;
}

运行迁移文件生成指令 ​

  1. 请确保你在.env文件中设置的DATABASE_HOST为开发数据库。
  2. 运行 pnpm run migrate:gen
  3. 当出现Success! Migration file created at migrations/<运行时的时间戳>-TinyPro.js命令后则表示迁移文件生成成功
  4. 运行 pnpm run mirgate:run指令或node migrate.js来应用迁移文件。当出现了 Now you can safely launched the project 字样。表示迁移文件已经被安全的应用到了数据库中。

初始化数据 ​

有些时候我们需要自动初始化一些数据(比如前端的默认国际化字段). 这些逻辑均需写在App.module.ts中AppModule类中的onModuleInit函数中。

国际化 ​

这里的国际化指的是报错信息的国际化

后端采用的是nestjs-i18n依赖库。国际化词条放在i18n/<lang>/xxx.json下

i18n
  enUS
    exception.json
    validation.json
  zhCN
    exception.json
    validation.json

目前仅支持enUS与zhCN两种语言,且fallback为enUS.

报错时候使用国际化词条 ​

后端服务遵循Restful规范,可以直接抛出错误使用HttpStatusCode来代替错误代码。如果需要使用国际化词条,请确保该词条已经存在于enUS|zhCN/exception.json文件内。假设有一个服务PolicyService需要抛出一个409错误。

  1. 添加国际化词条
  2. 在服务中注入I18nService
  3. 使用该词条
json
// zhCN/exception.json
{
  // 前面不做修改
  "policy":{
    "exists": "Policy已存在"
  }
}
ts
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { I18nTranslations } from '../.generate/i18n.generated';
import { I18nContext, I18nService } from 'nestjs-i18n';
@Injectable()
export class PolicyService {
  constructor(
    private readonly i18n: I18nService<I18nTranslations>
  ) {}
  createPolicy(){
    const exists = ...;
    if (exists){
      throw new HttpException(
        this.i18n.translate('exception.policy.exists', {
          lang: I18nContext.current().lang,
        }),
        HttpStatus.CONFLICT // 409
      )
    }
    //....
  }
}

接口权限管理 ​

Token管理 ​

凡是没有被Public修饰器修饰的接口,均会被auth/auth.guard.ts进行校验,如果token不存在、token过期、token不合法,均不允许访问。

权限控制 ​

如果一个接口没有被Permission修饰器进行修饰,那么这个接口是允许所有已经登录的用户访问。如果一个接口被Permission修饰器进行修饰,那么该接口仅允许拥有该权限的用户访问,其余用户会返回403错误代码

默认admin用户存在超级权限(*), 拥有该权限且已经登陆的用户可以访问任何接口。

例如

ts
@Controller('/policy')
export class PolicyController {
  @Get('/list')
  async getPolicy(){}
}

上述代码中GET /policy/list是一个不公开,不受保护的接口。我们可以使用Permission修饰器对他进行权限认证,当且仅当用户角色存在policy::get::list权限时才放行

ts
@Controller('/policy')
export class PolicyController {
  @Get('/list')
  @Permission('policy::get::list')
  async getPolicy(){}
}

这样一来GET /policy/list就只允许拥有policy::get::list权限的角色访问,其余角色访问则会返回一个403错误

但有些时候我们需要一个接口允许未登陆的用户访问。例如我们在登陆的时候经常需要获取免责声明,那么我们就可以写一个GET /policy接口,用于获取一个免责声明的法律条文。

所以我们可以添加如下

ts
@Controller('/policy')
export class PolicyController {
  @Get('/list')
  @Permission('policy::get::list')
  async getPolicies(){}
  @Get('/')
  @Public()
  async getPolicy(){}
}

这样一来GET /policy/list接口只允许登录且拥有policy::get::list权限的角色访问。GET /policy接口则允许未登陆的所有角色进行访问。

如果未来的某一天,我们需要让/policy/*都允许未登录的用户访问,那么我们可以这么写

ts
@Public()
@Controller('/policy')
export class PolicyController {
  @Get('/list')
  async getPolicies(){}
  @Get('/')
  async getPolicy(){}
}

这样一来,所有的policy接口都可以被未登录的用户访问了

遇到困难? ​

加官方小助手微信 opentiny-official,加入技术交流群

常见问题 ​

打包速度慢 ​

请阅读SWC

提示 Lock file exists, if you want init agin, please remove dist or dist/lock ​

  • 对于 1.x 用户来说可以直接删除 dist/lock 文件夹.
  • 对于 2.x 用户来说可以在redis中执行 DEL FLAG:INSTALL

docker 部署时数据库超时 ​

在新版本中我们加入了 wait4x 来检查 mysql 容器情况。但这并不能完全避免因为 mysql 启动过慢而导致的容器启动失败。在业务容器中我们设定的轮询时间为2s, 最多等待60s. 如果超时请按照如下检查表逐一排查

  1. MySQL容器是否启动成功?
  2. MySQL容器是否初始化成功?
  3. 业务容器环境变量是否正确?