Interact with MongoDB, pagination

  1. Inject model into service

    Use the @injectModel() decorator and pass in the schema name. Model<schema class name> is a fixed notation. Same here as express, but also write async/await to make Promises easier to manage and more readable.

    Enter this.coffeeModel. here to see all available methods. Compared to mongoose in express, this.coffeeModel here is equivalent to the original Schema name (capitalized), such as Store, Category, User, etc.


    In the service file:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    import { Injectable, NotFoundException } from '@nestjs/common';
    import { InjectModel } from '@nestjs/mongoose';
    import { Model } from 'mongoose';
    import { Coffee } from './entities/coffee.entity';

    @Injectable()
    export class CoffeesService {
    constructor(
    @injectModel(Coffee.name) private readonly coffeeModel: Model<Coffee>,
    ){}

    async findAll(){
    return this.coffeeModel.find().exec();
    };

    async findOne(id: string){
    const coffee = await this.coffeeModel.findOne({ _id: id}).exec();
    if(!coffee) {
    throw new NotFoundException(`Coffee #${id} not found`);
    }
    return coffee;
    };

    async create(createCoffeeDto: CreateCoffeeDto){ //Type verification
    const coffee = new this.coffeeModel( createCoffeeDto );
    return coffee.save();
    };

    async create(createCoffeeDto: CreateCoffeeDto){ //Another way of writing
    const coffee = await this.coffeeModel.create( createCoffeeDto );
    return coffee;
    };

    async update(id: string, updateCoffeeDto: UpdateCoffeeDto){
    const existingCoffee = await this.coffeeModel.findByIdAndUpdate({ _id: id }, { $set: updateCoffeeDto }, { new: true }).exec();
    if(!exsitingCoffee){
    throw new NotFoundException(`Coffee #${id} not found`);
    }
    return coffee;
    }

    async remove(id: string){
    const coffee = await this.findOne(id);
    return coffee.remove();
    }
    }

    $set: Update a field in the document instead of replacing it all.


    In remove , there is no need to remove the manual validation (!coffee) , as findOne will automatically return an error when it cannot find the target.


  1. Changes in Controller file

    First id should be changed from number to string. This is because in mongoDB _id is a string and not a number. If this is not changed, the ValidationPipe will try to automatically convert the “id” of any incoming object to a number, resulting in a lot of random errors.


  1. Pagination

    First create pagination: nest g class common/dto/pagination-query.dto --no-spec . The pagination is created with a new class name in order to hold things that are not tied to any specific field and can be reused by multiple controllers.

    In the pagination-query.dto file:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import { Type } from 'class-transformer';

    export class PaginationQueryDto {
    @IsOptional()
    @IsPositive()
    @Type(() => Number) //This can be removed by global setting implicit conversion
    limit: number;

    @IsOptional()
    @IsPositive()
    @Type(() => Number) //This can be removed by global setting implicit conversion
    offset: number;
    }

    In main.ts file:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    async function bootstrap() {
    const app = await NestFactory.create(AppModule);
    app.useGlobalPipes(
    new ValidationPipe({
    whitelist: true,
    transform: true,
    forbidNonwhitelisted: true,
    transformOptions: {
    enableImplicitConversion: true, //Set this item
    }
    })
    )
    }

    In the controller file:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Injectable()
    export class CoffeesService {
    constructor(
    @injectModel(Coffee.name) private readonly coffeeModel: Model<Coffee>,
    ){}

    async findAll(@Query() paginationQuery: PaginationQueryDto){
    return this.coffeeModel.findAll(paginationQuery).exec();
    };

    In the service file:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import { Injectable, NotFoundException } from '@nestjs/common';
    import { InjectModel } from '@nestjs/mongoose';
    import { Model } from 'mongoose';
    import { Coffee } from './entities/coffee.entity';

    @Injectable()
    export class CoffeesService {
    constructor(
    @injectModel(Coffee.name) private readonly coffeeModel: Model<Coffee>,
    ){}

    async findAll(paginationQuery: PaginationDto){
    const {limit, offset} = paginationQuery;
    return this.coffeeModel.find().skip(offset).limit(limit)exec();
    };
    }


  1. Sort syntax

    1
    2
    3
    4
    Post.find({}).sort('test').exec(function(err, docs) { ... });
    Post.find({}).sort([['date', -1]]).exec(function(err, docs) { ... });
    Post.find({}).sort({test: 1}).exec(function(err, docs) { ... });
    Post.find({}, null, {sort: {date: 1}}, function(err, docs) { ... });

Share