2-4:在Mikro-ORM中使用分页查询

5 阅读2分钟

上一章中,通过seeder实现了数据的自动生成以及crud操作。但是在使用findAll时,是将所有的数据都返回。一般来说,我们会通过分页的方式控制一次返回数据的数量。 根据官网的API手册,可以看到,FindAll可以传入FindOption,其中包括limit,offset,orderBy等参数。
在NestJS中定义查询参数,关键词为:@Query()

1.定义查询参数的Dto

dto文件夹下创建filter-todo.dto.ts

export class FilterTodoDto{
    page:number;
    limit:number;
    orderBy:'asc'|'desc'
}

2.修改controllerservice中的findAll函数

  @Get()
  findAll(@Query() filterTodoDto: FilterTodoDto) {
    return this.todosService.findAll(filterTodoDto);
  }
  async findAll(filterTodoDto: FilterTodoDto) {
    const { page, limit, orderBy } = filterTodoDto;
    const offset = (page - 1) * limit;
    return await this.todoRepository.findAll({
      limit,
      offset,
      orderBy: { id: orderBy },
    });
  }

3.增加Dto校验

使用class-validator和class-transform 首先安装pnpm add class-validator class-transform。然后在main.ts中添加管道

import { ValidationPipe } from '@nestjs/common';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(
    new ValidationPipe({
      whitelist: true,//如果前端传来的JSON数据中包括DTO中没有定义的字段,将多余字段丢掉
      forbidNonWhitelisted: true,//检测到多余字段后报错
      transform: true,//自动将传过来的数据实例化为DTO类
      transformOptions: {
        enableImplicitConversion: true,
      },
    }),
  );
  await app.listen(process.env.PORT ?? 3000);
}
bootstrap();

通过使用transform: true。我们就可以将原本的controller中的

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.todosService.findOne(+id);
  }

  @Patch(':id')
  update(@Param('id') id: string, @Body() updateTodoDto: UpdateTodoDto) {
    return this.todosService.update(+id, updateTodoDto);
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.todosService.remove(+id);
  }

改成,因为程序会自动帮我们在DTO中定义的id:number将JSON数据中的字符串类型转换为string类型。!!!注意,这里transfrom只会将我们这里简单的路径参数做类型转换,但是查询参数是无法类型转换的。(因此还需要加上transformOptions: { enableImplicitConversion: true, },)

  @Get(':id')
  findOne(@Param('id') id: number) {
    return this.todosService.findOne(id);
  }

  @Patch(':id')
  update(@Param('id') id: number, @Body() updateTodoDto: UpdateTodoDto) {
    return this.todosService.update(id, updateTodoDto);
  }

  @Delete(':id')
  remove(@Param('id') id: number) {
    return this.todosService.remove(id);
  }

然后在Dto中加上类型及校验

//create-todo.dto.ts
import { IsString, IsBoolean } from 'class-validator';
export class CreateTodoDto {
  @IsString()
  title: string;

  @IsString()
  content: string;

  @IsBoolean()
  isCompleted: boolean;
}

小心得。在实体类中,我们定义了id,title,content,isCompleted四个属性,为什么create-todo.dto.ts中只需要三个属性? 因为id是主键,每个数据的id都必须独一无二,因此这个工作应该是由数据库自己完成,而不是用户上传,因此前端的Dto不应该传入id

然后修改filter-todo.dto

import { IsEnum, IsInt, IsOptional } from 'class-validator';
enum OrderBy {
  ASC = 'asc',
  DESC = 'desc',
}
export class FilterTodoDto {
  @IsInt()
  @IsOptional()
  page?: number;

  @IsInt()
  @IsOptional()
  limit?: number;

  @IsOptional()
  @IsEnum(OrderBy)
  orderBy?: OrderBy;
}