NestJS 问题记录

以前写后端都是用 Express 的,但现在流行的是 NestJS,为了跟上潮流,也只能尝试 NestJS 了。对于新的技术栈,碰到问题是难免的,所以这里简要记录碰到的问题。

TypeORM

TypeORM 是一个统一和简化数据库操作的库,比手动拼 SQL 方便一些(熟练以后)。

实体字段如何与数据库不同

很多时候,后端返回给前端的数据名称需要与数据库不同,所以最好有一种映射方法,查询之后自动转换。TypeORM 可以在定义实体(Entity)的时候指定映射字段——Column 装饰器里指定 name。

@Column({ name: 'created_at' })
created: string;

连接多个数据库

网上有很多方法的介绍,比如 app.module 里定义多个 TypeOrmModule.forRoot,以此创建多个连接。我没用这种方式,因为我的库都在一台机器上,这种情况只需要在定义实体的时候指定数据库就行。

@Entity('user', {
  database: 'lab',
})

where 条件嵌套查询

手拼 SQL 的时候我是根据条件拼接不同的 OR 或者 AND 语句,用了 TypeORM 之后方便多了。比如根据条件是否存在调用 where 或者 andWhere 或者 orWhere。

// 关键字查询
if (keyword) {
  qb.where(
    '(poem.title LIKE :keyword OR poem.author LIKE :keyword OR poem.content LIKE :keyword)',
    { keyword: `%${keyword}%` },
  );
}
// 作者
if (author) {
  qb.andWhere('poem.author = :author', { author });
}
// 类型
if (type) {
  qb.andWhere('poem.type LIKE :type', { type: `%#${type}#%` });
}

调用很方便,并且即使 keyword 不存在,也不影响后续 andWhere 的调用。而对于嵌套的条件,直接用括号就可以,和手拼一样的(代码里 qb.where 部分)。

还有就是 LIKE 查询的写法。一开始我是这样写的:LIKE `’%#${type}#%’`,这种写法不行,即使把单引号去掉也不行,因为 type 部分会自动替换成包含单引号的实际值。因此,需要直接将值处理成最终的查询条件字符串——{ type: `%#${type}#%` }。

查询结果自动映射实体对象字段

对于创建的查询器,可以调用 execute() 方法执行,但这种方式得到的结果字段是类似于 poem_created_at 这种带有前缀的,并且没有将数据库字段转成我们我需要的字段。并且,如果需要 count,还需要手动调用 getCount() 方法。

其实,为了得到最终结果,只需要调用 getManyAndCount() 方法就可以了,这个方法返回一个数组,数组第一个元素是结果列表,第二个元素是 count 结果。

另外,如果只需要一个结果,直接 getOne() 方法就可以。

分页查询

对于分页,可以调用查询器的 limit() 方法和 offset() 方法。

class-validator

class-validator 是一个配合 NestJS 使用的校验库。

请求参数校验

一开始我以为请求参数直接根据 dto 的 ts 类型就可以校验了,比如:

export class ListReqDto {
  readonly keyword?: string;

  readonly page?: number;

  readonly size?: number;
}

但实际上,这样好像并不能触发校验,或者说实际上并没有定义校验条件。字段的具体限制需要添加各种装饰器实现。

export class ListReqDto {
  @IsString()
  @IsOptional()
  readonly keyword?: string;

  @IsNumber()
  @IsOptional()
  readonly page?: number;
}

另外,如果需要校验参数为枚举,可以使用 IsIn([xxx, xxx]) 装饰器。