Skip to content

B. TypeORM Basic CRUD

TypeORM merupakan ORM (Object Relational Mapping yang dapat berjalan pada NodeJS). Selain TypeOrm , ORM lain yang bisa digunakan pada nestjs adalah sequelize, prisma , knex dan lain-lain. untuk memulai menggukan TypeORM pada nestjs kita instalasi terlebih dahulu

terminal
npm install --save @nestjs/typeorm typeorm mysql2

Kemudian kita akan membuat file baru untuk menyimpan konfigurasi TypeOrm di aplikasi kita

0. Instalasi MYSQL dan PhpMyAdmin

Pada latihan ini kita akan lakukan instalasi mysql dan phpMyAdmin menggunakan docker. buatlah file dengan nama docker-compose.yml

docker-compose.yml
version: "3.9"
services:
  db:
    image: mysql:8.0
    container_name: mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_PASSWORD: root
      TZ: Asia/Jakarta
    ports:
      - 3308:3306
    networks:
      - internal_network
    volumes:
      - ./app/:/user

  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    container_name: phpmyadmin
    links:
      - db
    environment:
      PMA_HOST: db
      MYSQL_ROOT_PASSWORD: root
    restart: always
    ports:
      - 8081:80
    networks:
      - internal_network



networks:
  internal_network:
    external: true

Kemudian kita akan buat network pada docker terlebih dahulu

terminal
docker network create internal_network

Kemudian kita instalasi mysql dan phpMyAdmin pada docker mengggunakan docker compose

terminal
docker-compose up

Tunggu sampai selesai proses instalasi

2. Global Configuration

ita akan membahas dotenv, dimana file ini digunakan untuk memyimpan konfigurasi pada aplikasi kita. Kalau sebelum nya konfigurasi kita tulis secara hardcode pada koding seperti pada saat membuat konfig typeorm

Pertama kita instalasi dulu package untuk config

npm i --save @nestjs/config

2. Import Module Config pada app module

kita import pada app module sebagai global agar bisa diakses oleh semua module

app.module.ts

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule } from '@nestjs/config';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,  // konfigurasi is global untuk semua module
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

3. Buat File .env

buatlah file .env

.env
DB_HOST = localhost // alamat server mysql
DB_USERNAME = root  // username dari mysql
DB_PASSWORD = root  // password dari mysq;
DB_DATABASE = belajar_nest_js // nama database
DB_PORT = 3308  // port dari mysql

1. TypeOrm Config

Buatlah folder config pada folder src , kemudian buatlah file typeorm.config.ts

Alt text

typeorm.config.ts
import { TypeOrmModuleOptions } from '@nestjs/typeorm';
import { Auth } from '../auth-service.entity';
export const typeOrmConfig: TypeOrmModuleOptions = {
  type: 'mysql',
  host: process.env.DB_HOST,
  port: Number(process.env.DB_PORT), 
  username: process.env.DB_USERNAME, 
  password: process.env.DB_PASSWORD, 
  database: process.env.DB_DATABASE,
  entities: [Auth],
  synchronize: true,
  logging: true,
};

2. Import TypeOrmConfig pada app module

import module TypeOrm pada app.module.ts agar typeorm bisa digunakan pada aplikasi kita.

app.module.ts
import { Module } from "@nestjs/common";
import { AppController } from "./app.controller";
import { AppService } from "./app.service";
import { LatihanModule } from "./latihan/latihan.module";
import { BookModule } from "./book/book.module";
import { TypeOrmModule } from "@nestjs/typeorm";
import { typeOrmConfig } from "./config/typeorm.config";
import { ConfigModule } from '@nestjs/config';

@Module({
  imports: [ ConfigModule.forRoot({
      isGlobal: true,
    }),
    TypeOrmModule.forRootAsync({
      useFactory: async () => {
        const { typeOrmConfig } = await import('./config/typeorm.config');
        return typeOrmConfig;
      },
    }),, LatihanModule, BookModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

3. Membuat Book Entity

Kemudian kita akan membuat entity untuk membuat table pada database mysql.

Buatlah file dengan nama book.entity.ts pada folder book, seperti berikut

Alt text

book.entity.ts
import { Entity, BaseEntity, PrimaryGeneratedColumn, Column } from "typeorm";

@Entity()
export class Book extends BaseEntity {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  title: string;

  @Column()
  author: string;

  @Column()
  year: number;

  @Column({ type: "datetime", default: () => "CURRENT_TIMESTAMP" })
  created_at: Date;

  @Column({ type: "datetime", default: () => "CURRENT_TIMESTAMP" })
  updated_at: Date;
}

Kemudian kita import entity pada book.module.ts

book.module.ts
import { Module } from "@nestjs/common";
import { BookService } from "./book.service";
import { BookController } from "./book.controller";
import { TypeOrmModule } from "@nestjs/typeorm";
import { Book } from "./book.entity"; //import dari book.entity.ts

@Module({
  imports: [TypeOrmModule.forFeature([Book])], // import dengan TypeOrm For Feature
  providers: [BookService],
  controllers: [BookController],
})
export class BookModule {}

Kemudian kita lihat apakah tabel sudah terbuat atau tidak pada database

Alt text

Pada gambar tersebut terlihat kalau tabel sudah terbuat secara otomatis pada database. Jika kita mengalami kendala tabel tidak terbuat, silahkan cek kembali langkah-langkah di atas.

4. Menggunakan Book Entity pada BookService

Ketika kita sudah menggukana forFeature() pada module, selanjutnya kita akan menginject BookRepository ke dalam BookService dengan menggunakan @InjectRepository() decorator

book.service.ts
import { Injectable, NotFoundException } from '@nestjs/common';
import { ResponseSuccess } from 'src/interface/response';
import { CreateBookDto, UpdateBookDto } from './book.dto';
import { InjectRepository } from '@nestjs/typeorm'; // import injectReposity
import { Book } from './book.entity'; // import Book Entiy
import { Repository } from 'typeorm'; import //import repository
@Injectable()
export class BookService {

  //inject book repository ke service

  constructor(
    @InjectRepository(Book) private readonly bookRepository: Repository<Book>,
  ) {}

   //inject book repository ke service

  private books: {
    id?: number;
    title: string;
    author: string;
    year: number;
  }[] = [
    {
      id: 1,
      title: 'HTML CSS',
      author: 'ihsanabuhanifah',
      year: 2023,
    },
  ];

  getAllBooks(): ResponseSuccess {
    return {
      status: 'Success',
      message: 'List Buku ditermukan',
      data: this.books,
    };
  }


}

5. Membuat DTO pada pada fitur book

book.dto.ts
import { OmitType } from '@nestjs/mapped-types';
import { Type } from 'class-transformer';
import {
  IsArray,
  IsInt,
  IsNotEmpty,
  IsOptional,
  Length,
  ValidateNested,
  Min,
  Max,
} from 'class-validator';
import { PageRequestDto } from 'src/utils/dto/page.dto';
export class BookDto {
  id: number;

  @IsNotEmpty()
  title: string;

  @IsNotEmpty()
  author: string;

  @IsInt()
  @Min(2020)
  @Max(2023)
  year: number;
}

export class CreateBookDto extends OmitType(BookDto, ['id']) {}
export class UpdateBookDto extends OmitType(BookDto, ['id']) {}

export class createBookArrayDto {
  @IsArray()
  @ValidateNested({ each: true })
  @Type(() => CreateBookDto)
  data: CreateBookDto[];
}

6. Menambahkan Book ke Tabel dengan TypeOrm

Pada materi ini , kita akan berlatih menambahkan data ke tabel book pada database mengguankan method save() dari TypeOrm

Perhatikan method createBook pada bookService, sebelumnya kita menggunakan database semetera pada array book seperti koding di bawah

createBook(createBookDto: CreateBookDto): ResponseSuccess {
    const { title, author, year } = createBookDto;
    this.books.push({
      id: new Date().getTime(),
      title: title,
      author: author,
      year: year,
    });

    return {
      status: 'Success',
      message: 'Berhasil menambakan buku',
    };
  }

selanjutnya kita kan ubah tempat menyimpan data ke dalam database dan menampilkannya , maka seperti koding di bawah ini

book.service.ts
import {
  HttpException,
  HttpStatus,
  Injectable,
  NotFoundException,
} from '@nestjs/common';

...

async createBook(createBookDto: CreateBookDto): Promise<ResponseSuccess> {
    const { title, author, year } = createBookDto;

    try {
      await this.bookRepository.save({
        title: title,
        author: author,
        year: year,
      });
      return {
        status: 'Success',
        message: 'Berhasil menambakan buku',
      };
    } catch (err) {
      throw new HttpException('Ada Kesalahan', HttpStatus.BAD_REQUEST);
    }
  }

...

Pada kode diatas, kita ubah method createBook menjadi asynchronous karena saat proses penyimpanan data akan ada jeda waktu menunggu sampai ada response, ntuk menjadikan asyncronous kita cukup memberika keyword async.

Selanjutnya kita bikin kondisi jika penyimpanan gagal maka akan menampilkan pesan kesalahan "Ada Kesalahan" dengan kode 400.

book.controller.ts
import {
  Body,
  Controller,
  Delete,
  Get,
  Param,
  Post,
  Put,
  Query,
  UsePipes,
  ValidationPipe,
} from '@nestjs/common';
import { BookService } from './book.service';
import { CreateBookDto} from './book.dto';
import { Pagination } from 'src/utils/decorator/pagination.decorator';

@Controller('book')
export class BookController {
  constructor(private bookService: BookService) {}


  @Post('/create')
  createBook(@Body() payload: CreateBookDto) {
    return this.bookService.createBook(payload);
  }

}

Pengujian pada create

Alt text

payload
{
    "title" : "NestJS",
    "author" : "Ihsan",
    "year" : 2020
}

Kita cek apakah data berhasil masuk atau belum ke database

Alt text

6. Menampilkan seluruh data Book dengan TypeOrm

kita akan menampilkan data dengan method find() pada typeOrm.

book.service.ts
async getAllBooks(): Promise<ResponseSuccess> {
    const result = await this.bookRepository.find();
    return {
      status: 'Success',
      message: 'List Buku ditermukan',
      data: result,
    };
  }
book.controller.ts
import {
  Body,
  Controller,
  Delete,
  Get,
  Param,
  Post,
  Put,
  Query,
  UsePipes,
  ValidationPipe,
} from '@nestjs/common';
import { BookService } from './book.service';
import { CreateBookDto } from './book.dto';
import { Pagination } from 'src/utils/decorator/pagination.decorator';

@Controller('book')
export class BookController {
  constructor(private bookService: BookService) {}


 @Get('/list')
  findAllBook() {
    return this.bookService.getAllBooks();
  }

}

Pengujian pada Postman

Alt text

7. Menampilkan detail book dengan TypeOrm

book.service.ts
...
async getDetail(id: number): Promise<ResponseSuccess> {
    const detailBook = await this.bookRepository.findOne({
      where: {
        id,
      },
    });

    if (detailBook === null) {
      throw new NotFoundException(`Buku dengan id ${id} tidak ditemukan`);
    }
    return {
      status: 'Success',
      message: 'Detail Buku ditermukan',
      data: detailBook,
    };
  }
...
book.controller.ts
import {
  Body,
  Controller,
  Delete,
  Get,
  Param,
  Post,
  Put,
  Query,
  UsePipes,
  ValidationPipe,
} from '@nestjs/common';
import { BookService } from './book.service';
import { CreateBookDto} from './book.dto';
import { Pagination } from 'src/utils/decorator/pagination.decorator';

@Controller('book')
export class BookController {
  constructor(private bookService: BookService) {}


  @Get('detail/:id')
  findOneBook(@Param('id') id: string) {
    return this.bookService.getDetail(Number(id));
  }

}

Pengujian pada Postman

Alt text

8. Mengupdate book dengan TypeOrm

book.service.ts
async updateBook(
    id: number,
    updateBookDto: UpdateBookDto,
  ): Promise<ResponseSuccess> {
    const check = await this.bookRepository.findOne({
      where: {
        id,
      },
    });

    if (!check)
      throw new NotFoundException(`Buku dengan id ${id} tidak ditemukan`);

    const update = await this.bookRepository.save({ ...updateBookDto, id: id });
    return {
      status: `Success `,
      message: 'Buku berhasil di update',
      data: update,
    };
  }
book.controller.ts
import {
  Body,
  Controller,
  Delete,
  Get,
  Param,
  Post,
  Put,
  Query,
  UsePipes,
  ValidationPipe,
} from '@nestjs/common';
import { BookService } from './book.service';
import { CreateBookDto, UpdateBookDto} from './book.dto';
import { Pagination } from 'src/utils/decorator/pagination.decorator';

@Controller('book')
export class BookController {
  constructor(private bookService: BookService) {}


 @Put('update/:id')
  updateBook(@Param('id') id: string, @Body() updateBookDto: UpdateBookDto) {
    return this.bookService.updateBook(Number(id), updateBookDto);
  }
}

Pengujian pada Update

Alt text

payload
{
    "title" : "NestJS up",
    "author" : "Ihsan Update",
    "year" : 2023
}

Pengujian pada after update Setelah proses update berhasil, kita akan cek kembali menggunakan endpoint detail.

Alt text

Pada gambar di atas, kita sudah berhasil merubah data sesuai yang di update

9. Menghapus book dengan TypeOrm

book.service.ts
async deleteBook(id: number): Promise<ResponseSuccess> {
    const check = await this.bookRepository.findOne({
      where: {
        id,
      },
    });

    if (!check)
      throw new NotFoundException(`Buku dengan id ${id} tidak ditemukan`);
    await this.bookRepository.delete(id);
    return {
      status: `Success `,
      message: 'Berhasil menghapus buku',
    };
  }
book.controller.ts
import {
  Body,
  Controller,
  Delete,
  Get,
  Param,
  Post,
  Put,
  Query,
  UsePipes,
  ValidationPipe,
} from '@nestjs/common';
import { BookService } from './book.service';
import { CreateBookDto, UpdateBookDto} from './book.dto';
import { Pagination } from 'src/utils/decorator/pagination.decorator';

@Controller('book')
export class BookController {
  constructor(private bookService: BookService) {}


  @Delete('delete/:id')
  deleteBook(@Param('id') id: string) {
    return this.bookService.deleteBook(+id);
  }

Pengujian pada Delete

Alt text

10. Menambahkan Banyak Buku ke table dengan TypeOrm

book.service.ts
...

async bulkCreate(payload: createBookArrayDto): Promise<ResponseSuccess> {
    try {
      let berhasil = 0;
      let gagal = 0;
      await Promise.all(
        payload.data.map(async (data) => {
          try {
            await this.bookRepository.save(data);

            berhasil += 1;
          } catch {
            gagal += 1;
          }
        }),
      );

      return this._success(`Berhasil menyimpan ${berhasil} dan gagal ${gagal}`);
    } catch {
      throw new HttpException('Ada Kesalahan', HttpStatus.BAD_REQUEST);
    }
  }

...
book.controller.ts
import {
  Body,
  Controller,
  Delete,
  Get,
  Param,
  Post,
  Put,
  Query,
  UsePipes,
  ValidationPipe,
} from '@nestjs/common';
import { BookService } from './book.service';
import { CreateBookDto, UpdateBookDto, createBookArrayDto}  from './book.dto';
import { Pagination } from 'src/utils/decorator/pagination.decorator';

@Controller('book')
export class BookController {
  constructor(private bookService: BookService) {}


  @Post('/create/bulk')
  bulkCreateBook(@Body() payload: createBookArrayDto) {
    return this.bookService.bulkCreate(payload);
  }

Pengujian pada Postman

Alt text

payload
{
    "data" : [
    {
        "title": "NestJS For Backend",
        "author": "Ihsan",
        "year": 2023
    },
    {
        "title": "Become Network Engineer",
        "author": "Fathi",
        "year": 2021
    },
    {
        "title": "HTML CSS",
        "author": "Nur",
        "year": 2022
    },
    {
        "title": "TypeScript",
        "author": "Ihsan",
        "year": 2023
    },
    {
        "title": "Server Admin",
        "author": "Raihan",
        "year": 2022
    },
    {
        "title": "Database MySQL",
        "author": "Akbar",
        "year": 2023
    },
    {
        "title": "React Developer",
        "author": "Nur",
        "year": 2023
    },
    {
        "title": "NextJs Developer",
        "author": "Ihsan",
        "year": 2023
    }
]
}

11. Membuat Pagination(Paging) pada menampilan semua data

Ketika membuat REST API untuk menampilkan semua data di tabel, maka kita perlu menggunakan paging agar query di backend tidak terlalu berat dan kita bisa membatasi berapa data yang ditampilkan dalam satu kali query.

Pada materi ini kita akan membahas bagaiaman membuat paging dengan typeorm dan nestjs

Langkah Pertama kita buat dulu response type untuk pagination

src/interface/response/response.interface.ts
import { HttpStatus } from "@nestjs/common";

export interface ResponseSuccess {
  statusCode?: HttpStatus;
  status: string;
  message: string;
  data?: any;
}

export interface ResponsePagination extends ResponseSuccess {
  pagination: {
    total: number;
    page: number;
    pageSize: number;
  };
}

Jadi ketika pagination kita wajibkan return memiliki object pagination denga property total data, page saat ini dan berapa data yang ditampilkan (pageSize)

Langkah Kedua kita buat dto untuk pagination , dengan membuat folder baru utils/dto

Alt text

src/utils/dto/page.dto.ts
import { Type } from "class-transformer";
import { IsInt } from "class-validator";

export class PageRequestDto {
  @IsInt()
  @Type(() => Number)
  page = 1;

  @IsInt()
  @Type(() => Number)
  pageSize = 10;
}

Langkah Ketiga kita buat dto findBookDto dengan mengextends PageRequestDto

book.dto.ts
import { OmitType } from "@nestjs/mapped-types";
import { IsInt, IsNotEmpty, Min, Max, Length } from "class-validator";
import { PageRequestDto } from "src/utils/dto/page.dto";
export class BookDto {
  id: number;

  @IsNotEmpty()
  @Length(4, 10)
  title: string;

  @IsNotEmpty()
  author: string;

  @IsInt()
  @Min(2020)
  @Max(2023)
  year: number;
}

...

export class FindBookDto extends PageRequestDto {}

...

Langkah Ke Empat kita ubah koding findAllBook pada controller dan service

book.service.ts
import {
  HttpException,
  HttpStatus,
  Injectable,
  NotFoundException,
} from '@nestjs/common';
import { ResponsePagination, ResponseSuccess } from 'src/interface/response';
import { CreateBookDto, FindBookDto, UpdateBookDto } from './book.dto';
import { InjectRepository } from '@nestjs/typeorm';
import { Book } from './book.entity';
import { Repository } from 'typeorm';

@Injectable()
export class BookService {
  constructor(
    @InjectRepository(Book) private readonly bookRepository: Repository<Book>,
  ) {}

  async getAllBooks(query: FindBookDto): Promise<ResponsePagination> {
    console.log('uqwey', query);
    const { page, pageSize } = query;
    const total = await this.bookRepository.count();

    const result = await this.bookRepository.find({
      skip: (Number(page) - 1) * Number(pageSize),
      take: Number(pageSize),
    });

    return {
      status: 'Success',
      message: 'List Buku ditermukan',
      data: result,
      pagination: {
        total: total,
        page: Number(page),
        pageSize: Number(pageSize),
      },
    };
  }


  ...
}
book.controller.ts
import {
  Body,
  Controller,
  Delete,
  Get,
  Param,
  Post,
  Put,
  Query,
  UsePipes,
  ValidationPipe,
} from "@nestjs/common";
import { BookService } from "./book.service";
import { CreateBookDto, FindBookDto, UpdateBookDto } from "./book.dto";

@Controller("book")
export class BookController {
  constructor(private bookService: BookService) {}

  @Get("/list")
  findAllBook(@Query() findBookDto: FindBookDto) {
    return this.bookService.getAllBooks(findBookDto);
  }
}

Pengujian pada Postman

Alt text

12. Implementasi Custom Decorator untuk meyederhanakan Paging

Selain mengguankan decorator bawaan seperti @Body(), @Query(), @Param() pada controler, kita juga bisa membuat Custom Decorator seperti pada dokumentasi https://docs.nestjs.com/custom-decorators. Pada contoh kasus kali ini, kita akan membuat Custom Decorator untuk paging sehingga, pada setiap service kita tidak perlu menghitung ulang page dan limit untuk paging

book.service.ts
async getAllBooks(query: FindBookDto): Promise<ResponsePagination> {
   ...

    const result = await this.bookRepository.find({
      skip: (Number(page) - 1) * Number(pageSize),
      take: Number(pageSize),
    });

  ...
  }

Perhatikan pada bagian skip, disitu kita harus menghitung limit .Bayangkan jika kita membuat fitur ini pada module lain, maka kita harus selalu menghitung limit. Tentu hal ini tidak efektif karena harus melalukan pekerjaan yang sama secara berulang. Pada materi ini kita akan membuat custom decorator agar kita tidak perlu menghitung ulang limit, namun nanti kita hanya tinggal menggunakan saja pada setiap service.

Pertama buatlah folder decorator pada folder utils

src/utils/decorator/pagination.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const Pagination = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();

    if (!!request.query.page === false) {   //memberikan nilai default 1 jika tidak dikirim client
      request.query.page = 1;
    }
    if (!!request.query.pageSize === false) { //memberikan nilai default 10 jika tidak dikirim client
      request.query.pageSize = 10;
    }

    request.limit =
      (Number(request.query.page) - 1) * Number(request.query.pageSize);
    request.query.pageSize = Number(request.query.pageSize);
    request.query.page = Number(request.query.page);
    return request.query;
  },
);

Pada kode di atas , kita menghitung limit dan kita return hasilnya pada decorator pagination

Selanjutnya kita perbaharui kode di main.ts larena kita menggunakan custom decorator maka kita harus mengaktifkan validateCustomDecorators menjadi true seperti pada kode di bawah.

main.ts

main.ts
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
import { ValidationPipe } from "@nestjs/common";

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(
    new ValidationPipe({
      whitelist: true,
      forbidUnknownValues: true,
      transform: true,
      validateCustomDecorators: true,
      transformOptions: {
        enableImplicitConversion: true,
      },
    })
  );
  await app.listen(5002);
}
bootstrap();

Selanjutnya kita ganti decorator @Query() pada findAllBook di book.controller.ts dengan custom decorator Pagination() yang kita buat.

book.controller.ts
import {
  Body,
  Controller,
  Delete,
  Get,
  Param,
  Post,
  Put,
  Query,
  UsePipes,
  ValidationPipe,
} from '@nestjs/common';
import { BookService } from './book.service';
import { CreateBookDto, FindBookDto, UpdateBookDto } from './book.dto';
import { Pagination } from 'src/utils/decorator/pagination.decorator';

@Controller('book')
export class BookController {
  constructor(private bookService: BookService) {}

  @Get('/list')
  findAllBook(@Pagination() findBookDto: FindBookDto) {
    return this.bookService.getAllBooks(findBookDto);
  }

  ...
}

Implementasikan limit dan pageSize pada book.service.ts

book.service.ts
async getAllBooks(query: FindBookDto): Promise<ResponsePagination> {
    const { page, pageSize, limit } = query;
    const total = await this.bookRepository.count();

    const result = await this.bookRepository.find({
      skip: limit,
      take: pageSize,
    });

    return {
      status: 'Success',
      message: 'List Buku ditermukan',
      data: result,
      pagination: {
        total: total,
        page: page,
        pageSize: pageSize,
      },
    };
  }

saat kita nenambahkan limit pada query maka akan muncul error, untuk mengatasi hal tersebut kita perbaharui page.dto.ts

utils/dto/page.dto.ts
import { Type } from "class-transformer";
import { IsInt, IsOptional } from "class-validator";

export class PageRequestDto {
  @IsInt()
  @Type(() => Number)
  page = 1;

  @IsInt()
  @Type(() => Number)
  pageSize = 10;

  @IsInt()
  @IsOptional()
  limit;
}

Pengujian pada Postman

Alt text

13. Membuat Filter page getAllBook

Seringkali ketika menampilkan data , kita membutuhkan filter fitur yang bisa dikombinasikan atau salah satu saja. Contoh pada kasus book kita kita bisa memcari berdasarkan salah satu dari author, title, year atau kombinasi ketiga nya.

Pada materi kali ini kita akan membuar fitur filter berdasrkan title, author, dan range tahun terbit seperti terlihat pada postman

Alt text

Kita tambahkan option title, author, from_year, to_year pada book.dto.ts

book.dto.ts
import { OmitType } from "@nestjs/mapped-types";
import { Type } from "class-transformer";
import {
  IsInt,
  IsNotEmpty,
  Min,
  Max,
  Length,
  IsOptional,
} from "class-validator";
import { PageRequestDto } from "src/utils/dto/page.dto";
export class BookDto {
  id: number;

  @IsNotEmpty()
  @Length(4, 10)
  title: string;

  @IsNotEmpty()
  author: string;

  @IsInt()
  @Min(2020)
  @Max(2023)
  year: number;
}

export class CreateBookDto extends OmitType(BookDto, ["id"]) {}
export class UpdateBookDto extends OmitType(BookDto, ["id"]) {}
export class FindBookDto extends PageRequestDto {
  @IsOptional()
  title: string;

  @IsOptional()
  author: string;

  @IsOptional()
  @IsInt()
  @Type(() => Number)
  from_year: number;

  @IsOptional()
  @IsInt()
  @Type(() => Number)
  to_year: number;
}

Selanjutkan kita tambahkan condition statement pada method find tyoeorm

book.service.ts
async getAllBooks(query: FindBookDto): Promise<ResponsePagination> {
    const { page, pageSize, limit, title, author, from_year, to_year } = query;

    console.log('q', query);
    const total = await this.bookRepository.count();

    const filter: {
      [key: string]: any;
    } = {};

    if (title) {
      filter.title = Like(`%${title}%`);
    }
    if (author) {
      filter.author = Like(`%${author}%`);
    }

    if (from_year && to_year) {
      filter.year = Between(from_year, to_year);
    }

    if (from_year && !!to_year === false) {
      filter.year = Between(from_year, from_year);
    }

    const result = await this.bookRepository.find({
      where: filter,
      skip: limit,
      take: pageSize,
    });

    return {
      status: 'Success',
      message: 'List Buku ditermukan',
      data: result,
      pagination: {
        total: total,
        page: page,
        pageSize: pageSize,
      },
    };
  }

Pengujian Postman

Alt text