Skip to content

A. Tambah Produk dan List Produk

Pada materi ini , kita akan membuat endpoint untuk CRUD pada tabel produk

Alt text

1. Membuat Module, Controller, Service

terminal
npx nest g module app/produk
npx nest g controller app/produk
npx nest g service app/produk

2. Membuat produk.entity.ts

produk.entity.ts
import {
  Entity,
  BaseEntity,
  PrimaryGeneratedColumn,
  Column,
  ManyToOne,
  JoinColumn,
} from 'typeorm';
import { User } from '../auth/auth.entity';
import { Kategori } from '../kategori/kategori.entity';

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

  @Column({ nullable: false })
  barcode: string;

  @ManyToOne(() => Kategori)
  @JoinColumn({ name: 'kategori_id' })
  kategori: Kategori;

  @Column({ nullable: false })
  nama_produk: string;
  @Column({ type: 'text', nullable: false })
  deskripsi_produk: string;

  @Column({ type: 'double', precision: 18, scale: 2, nullable: false })
  harga: number;

  @Column()
  stok: number;

  @Column({ nullable: true })
  foto: string;

  @ManyToOne(() => User)
  @JoinColumn({ name: 'created_by' })
  created_by: User;

  @ManyToOne(() => User)
  @JoinColumn({ name: 'updated_by' })
  updated_by: User;

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

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

3. Import Entity pada produk module

app.produk.ts
import { Module } from '@nestjs/common';
import { ProdukService } from './produk.service';
import { ProdukController } from './produk.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Produk } from './produk.entity';

@Module({
  imports: [TypeOrmModule.forFeature([Produk])],
  providers: [ProdukService],
  controllers: [ProdukController],
})
export class ProdukModule {}

4. Membuat DTO pada fitur produk

produk.dto.ts
import { OmitType } from '@nestjs/mapped-types';
import { Type } from 'class-transformer';
import {
  IsArray,
  IsInt,
  IsNotEmpty,
  IsNumber,
  IsOptional,
  IsString,
  Length,
  ValidateNested,
} from 'class-validator';
import { PageRequestDto } from 'src/utils/dto/page.dto';

export class ProdukDto {
  @IsInt()
  id: number;

  @IsString()
  @IsNotEmpty()
  @Length(8)
  barcode: string;

  @IsString()
  @IsNotEmpty()
  nama_produk: string;

  @IsNumber()
  @IsNotEmpty()
  kategori_id: number;

  @IsString()
  @IsNotEmpty()
  deskripsi_produk: string;

  @IsNotEmpty()
  @IsNumber()
  harga: number;

  @IsNotEmpty()
  @IsNumber()
  stok: number;

  @IsOptional()
  @IsString()
  foto: string;
}

export class CreateProdukDto extends OmitType(ProdukDto, ['id']) {}
export class CreateProdukArrayDto {
  @IsArray()
  @ValidateNested({ each: true })
  @Type(() => CreateProdukDto)
  data: CreateProdukDto[];
}
export class findAllProduk extends PageRequestDto {
  @IsString()
  @IsOptional()
  nama_produk: string;

  @IsString()
  @IsOptional()
  deskripsi_produk: string;

  @IsOptional()
  @IsNumber()
  @Type(() => Number)
  dari_harga: number;

  @IsOptional()
  @IsNumber()
  @Type(() => Number)
  sampai_harga: number;

  @IsString()
  @IsOptional()
  keyword: string;
}

5. Endpoint menambah produk secara bulk dan Menampilkan Produk

Pada materi ini, kita akan membuat enpoint untuk,

  • Menambakan produk secara bulk (CREATE)
  • Menampilkan data pada tabel kategori dengan Pagination dan filter berdasarkan field nama_produk, deskripsi_produk, range harga
import { HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { InjectRepository } from '@nestjs/typeorm';
import BaseResponse from 'src/utils/response/base.response';
import { Produk } from './produk.entity';
import { Between, Like, Repository } from 'typeorm';
import { CreateProdukArrayDto, findAllProduk } from './produk.dto';
import { ResponsePagination, ResponseSuccess } from 'src/interface/response';

@Injectable()
export class ProdukService extends BaseResponse {
  constructor(
    @InjectRepository(Produk)
    private readonly produkRepository: Repository<Produk>,
    @Inject(REQUEST) private req: any,
  ) {
    super();
  }

  async createBulk(payload: CreateProdukArrayDto): Promise<ResponseSuccess> {
    try {
      let berhasil = 0;
      let gagal = 0;
      await Promise.all(
        payload.data.map(async (data) => {
          const dataSave = {
            ...data,
            kategori: {
              id: data.kategori_id,
            },
            created_by: {
              id: this.req.user.id,
            },
          };

          try {
            await this.produkRepository.save(dataSave);

            berhasil += 1;
          } catch (err) {
            console.log('err', err);
            gagal += 1;
          }
        }),
      );

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

  async findAll(query: findAllProduk): Promise<ResponsePagination> {
    const {
      page,
      pageSize,
      limit,
      nama_produk,
      dari_harga,
      sampai_harga,
      deskripsi_produk,
    } = query;

    const filterQuery = {};
    if (deskripsi_produk) {
      filterQuery.deskripsi_produk = Like(`%${deskripsi_produk}%`);
    }
    if (nama_produk) {
      filterQuery.nama_produk = Like(`%${nama_produk}%`);
    }
    if (dari_harga && sampai_harga) {
      filterQuery.harga = Between(dari_harga, sampai_harga);
    }
    if (dari_harga && !!sampai_harga === false) {
      filterQuery.harga = Between(dari_harga, dari_harga);
    }
    const total = await this.produkRepository.count({
      where: filterQuery,
    });
    const result = await this.produkRepository.find({
      where: filterQuery,
      relations: ['created_by', 'updated_by', 'kategori'],
      select: {
        id: true,
        nama_produk: true,
        deskripsi_produk: true,
        stok: true,
        harga: true,
        kategori: {
          id: true,
          nama_kategori: true,
        },
        created_by: {
          id: true,
          nama: true,
        },
        updated_by: {
          id: true,
          nama: true,
        },
      },
      skip: limit,
      take: pageSize,
    });
    return this._pagination('OK', result, total, page, pageSize);
  }
}
produk.controller.ts
import { Body, Controller, Get, Post, UseGuards } from '@nestjs/common';
import { ProdukService } from './produk.service';
import { CreateProdukArrayDto, findAllProduk } from './produk.dto';
import { JwtGuard } from '../auth/auth.guard';
import { Pagination } from 'src/utils/decorator/pagination.decorator';

@UseGuards(JwtGuard)
@Controller('produk')
export class ProdukController {
  constructor(private produkService: ProdukService) {}

  @Post('create-bulk')
  async createBulk(@Body() payload: CreateProdukArrayDto) {
    return this.produkService.createBulk(payload);
  }

  @Get('list')
  async findAll(@Pagination() query: findAllProduk) {
    return this.produkService.findAll(query);
  }
}

6.Pengujian pada Postman

Testing create produk pada postman

Alt text

payload
{
    "data": [
        {
            "barcode": "12092299",
            "nama_produk": "Iphone 12",
            "deskripsi_produk": "Produk berhkualis tinggi",
            "harga": 17000000.22,
            "stok": 12,
            "kategori_id": 1
        },
        {
            "barcode": "12092244",
            "nama_produk": "Changhong ",
            "deskripsi_produk": "Produk berhkualis tinggi",
            "harga": 1000000.22,
            "stok": 12,
            "kategori_id": 2
        },
        {
            "barcode": "12092249",
            "nama_produk": "Asus Oleh 12 ",
            "deskripsi_produk": "Produk berhkualis tinggi",
            "harga": 1500000.22,
            "stok": 15,
            "kategori_id": 2
        }
    ]
}

Testing list produk dengan filter nama_produk, deskripsi_produk dan range harga

Alt text

7.Menambah fitur search dengan keyword pada list produk

Pada contoh sebelumnya kita sudah berhasil membuat filter berdasarkan nama field dan bisa kita komninasikan. Selanjutnya kita akan membuat fitur search pada list produk. Secara teknis fitur search akan mencari kolom yang yang pada tabel dengan pendekatan OR.

Bagamana where OR pada TypeORM , contohnya seperti di bawah

Example
const result = await productRepository.find({
            where: [
            {price: LessThan(100)},
            {color: 'red'}
           ]
})

Arti pada kode diatas adalah tampilkan semua data yang memiliki price dibawah 100 atau color nya adala red. Ketika kita menggunakan logika or maka pada bagian where ditulis dengan array dan nama field dimasukan kedalam object beserta valuenya.

Kita akan implementasikan bagaimana membuat pencarian berdasarkan keyword pada list produk.

Impementasikan query string keyword

kita akan buat logic pada method findAll di produk.service.ts , jika ada keyword pada query string maka where yang akan digunakan adalah where dengan logic and dan logic filter tidak digunakan.

produk.service.ts
async findAll(query: findAllProduk): Promise<ResponsePagination> {
    const {
      page,
      pageSize,
      limit,
      nama_produk,
      dari_harga,
      sampai_harga,
      deskripsi_produk,
      keyword,
    } = query;

    const filterQuery  = {};
    const filterKeyword = [];

    if (keyword) {
      filterKeyword.push(
        {
          nama_produk: Like(`%${keyword}%`),
        },
        {
          harga: Like(`%${keyword}%`),
        },
        {
          deskripsi_produk: Like(`%${keyword}%`),
        },
      );
    } else {
      if (deskripsi_produk) {
        filterQuery.deskripsi_produk = Like(`%${deskripsi_produk}%`);
      }
      if (nama_produk) {
        filterQuery.nama_produk = Like(`%${nama_produk}%`);
      }
      if (dari_harga && sampai_harga) {
        filterQuery.harga = Between(dari_harga, sampai_harga);
      }
      if (dari_harga && !!sampai_harga === false) {
        filterQuery.harga = Between(dari_harga, dari_harga);
      }
    }

    const total = await this.produkRepository.count({
      where: keyword ? filterKeyword : filterQuery,  
    });
    const result = await this.produkRepository.find({
      where: keyword ? filterKeyword : filterQuery,
      relations: ['created_by', 'updated_by', 'kategori'],
      select: {
        id: true,
        nama_produk: true,
        deskripsi_produk: true,
        stok: true,
        harga: true,
        kategori: {
          id: true,
          nama_kategori: true,
        },
        created_by: {
          id: true,
          nama: true,
        },
        updated_by: {
          id: true,
          nama: true,
        },
      },
      skip: limit,
      take: pageSize,
    });
    return this._pagination('OK', result, total, page, pageSize);
  }

Pengujian pada Postman

Ketika keyword dikirim dengan value

Alt text

Hasil query sesuai karena nama_produk iphone berdasarkan keyword yang dikirim

Ketika tanpa keyword

Alt text

Hasil query sesuai karena menampilkan nama_produk asus sesuai dengan filter yang dikirim.