A. Tambah Produk dan List Produk
Pada materi ini , kita akan membuat endpoint untuk CRUD pada tabel produk
1. Membuat Module, Controller, Service
2. Membuat 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
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
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);
}
}
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
{
"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
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
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.
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
Hasil query sesuai karena nama_produk iphone berdasarkan keyword yang dikirim
Ketika tanpa keyword
Hasil query sesuai karena menampilkan nama_produk asus sesuai dengan filter yang dikirim.