Pengenalan Nest.js dan Kenapa Penting Belajarnya
Nest.js adalah framework progressive yang dibangun di atas Express.js untuk membuat aplikasi backend yang scalable, testable, dan maintainable. Kalau Express itu kanvas putih yang fleksibel, Nest.js adalah template dengan struktur yang sudah jelas dan terorganisir.
Tiga alasan penting belajar Nest.js:
Pertama, Nest.js menggunakan TypeScript secara default—bukan JavaScript biasa. Ini memberikan type-safety yang kuat dan mengurangi bug yang sulit dilacak. Kamu akan menangkap error lebih awal sebelum aplikasi berjalan.
Kedua, struktur Nest.js terorganisir dengan modul, controller, service, dan dependency injection. Ini membuat proyekmu tetap rapi meski berkembang besar. Kode menjadi mudah di-maintain dan dikembangkan oleh multiple developer.
Ketiga, Nest.js benar-benar dipakai di industri untuk aplikasi production. Jadi belajar Nest.js berarti belajar best practice yang akan kamu gunakan di pekerjaan profesional nanti.
Keunggulan Nest.js Dibanding Express.js
Express.js sangat fleksibel dan minimalis, sempurna untuk prototype cepat. Tapi di proyek tim besar atau jangka panjang, kebebasan ini menjadi masalah. Setiap orang bisa organize kode berbeda, akibatnya proyekmu jadi chaos.
Nest.js berbeda. Ia mengikuti architecture pattern yang ketat, mirip Laravel atau Django. Kamu nggak perlu bingung lagi—sudah ada template dan best practices yang proven.
Keunggulan spesifik Nest.js dibanding Express:
TypeScript bawaan. Nest.js wajib pakai TypeScript, bukan optional. Express bisa pakai tapi butuh setup ekstra. Dengan Nest.js, error ketangkap lebih awal sebelum kode dijalankan. Type checking yang ketat mencegah banyak bug production.
Struktur yang jelas. Controller, service, dan module sudah terbagi dengan rapih. Express cuma punya routes dan controllers tanpa service layer yang konsisten. Di Nest.js, kamu tahu persis file mana yang handling business logic, mana yang handle request, dan mana yang handle database query.
Dependency Injection built-in. Ini membuat unit testing jauh lebih mudah. Kamu bisa inject mock dependency dengan gampang. Express nggak punya fitur ini sehingga butuh setup manual. Dengan DI, testing jadi lebih simple dan code lebih modular.
Decorators powerful. Nest.js menggunakan decorators TypeScript yang membuat kode lebih readable dan memudahkan attach metadata ke class atau method dengan gampang. Ini membuat custom middleware dan guards menjadi elegan.
Testing utilities bawaan. Nest.js sudah punya utilities testing yang bagus. Express kamu perlu setup Jest atau Mocha manual. Di Nest.js, testing framework sudah integrated dan encourage best practices dari awal.
Scalability untuk tim besar. Karena struktur konsisten, semua developer mengikuti pattern yang sama. Kode lebih predictable dan mudah di-maintain. Di Express, tanpa disiplin tim, kode bisa berantakan. Dengan Nest.js, setiap developer akan write code dengan cara yang konsisten.
Kesimpulan: Express cocok untuk quick prototyping dan side projects. Nest.js cocok untuk production-ready application yang dimaintain jangka panjang oleh tim besar.
Use Case dan Kapan Menggunakan Nest.js
Gunakan Nest.js untuk:
- REST API skala besar. Kalau kamu build backend untuk aplikasi e-commerce yang handle ribuan transaksi setiap hari, struktur Nest.js memudahkan kamu scale dan maintain code seiring pertumbuhan platform. Arsitektur yang terstruktur membuat performance optimization lebih mudah dilakukan.
- Microservices architecture. Nest.js memiliki dukungan excellent untuk membangun multiple services dengan pattern yang sama. Kamu bisa koordinasi mereka dengan efisien melalui message queue atau gRPC.
- Aplikasi real-time dengan WebSocket. Kalau aplikasimu butuh real-time communication seperti chat atau live notification, Nest.js punya gateway yang powerful dan terstruktur. WebSocket communication jadi mudah dimaintain.
- Proyek tim besar yang butuh consistency. Semua developer mengikuti pola yang sama, code lebih mudah di-review, dan onboarding developer baru lebih cepat.
- Enterprise application dengan security tinggi. Nest.js punya fitur-fitur built-in seperti testing utilities, logging, dan error handling yang sudah follow best practices dari awal.
Jangan gunakan Nest.js untuk:
- Quick prototyping yang butuh kecepatan. Kalau kamu hanya perlu membuat prototype cepat untuk validate idea, setup Nest.js mungkin overkill. Express akan lebih cepat tanpa boilerplate yang banyak.
- Aplikasi sangat simpel dengan 2-3 endpoint saja. Nest.js akan membuat file lebih banyak dan lebih kompleks dari yang diperlukan. Express sudah cukup untuk use case ini.
- Single developer dengan deadline ketat. Learning curve Nest.js lebih tinggi dari Express. Kalau kamu sendiri dengan waktu terbatas, Express lebih praktis.
- Legacy system yang kurang terstruktur. Struktur rigid Nest.js mungkin agak friction ketika harus integrate dengan legacy code yang messy.
Takeaway: Untuk pemula yang serius jadi backend developer, Nest.js adalah investasi terbaik. Ya, learning curve-nya lebih tinggi dari Express, tapi kamu belajar best practice yang benar dari awal. Ini jauh lebih baik daripada harus re-learn nanti ketika sudah bekerja di perusahaan sebenarnya.
Jadi sudah siap? Lanjutkan ke bagian berikutnya di mana kita install Nest.js dan buat project pertama. Akan sangat seru!
Pengetahuan Dasar yang Diperlukan
Sebelum mulai, ada beberapa pengetahuan yang perlu kamu kuasai untuk memudahkan proses setup.
Node.js. Nest.js dibangun di atas Node.js, jadi kamu harus paham cara menjalankan file JavaScript, menggunakan npm atau yarn, dan konsep dasar seperti modules, callbacks, dan promises. Kalau belum familiar, pelajari basics Node.js terlebih dahulu.
TypeScript basic. Nest.js menggunakan TypeScript, jadi kamu perlu tahu concepts dasar seperti interfaces, types, dan classes. Kamu nggak perlu jadi expert—just enough to membaca syntax dan understand type annotations sudah cukup.
JavaScript modern features. Arrow functions, destructuring, async-await, dan spread operator adalah tools yang akan digunakan setiap hari di Nest.js development.
Jadi ringkasannya, kamu perlu: Node.js basic knowledge, TypeScript basic understanding, dan JavaScript modern features. Kalau sudah familiar dengan semua ini, kamu siap lanjut ke tahap setup.
Instalasi Node.js dan npm

Langkah pertama adalah install Node.js. Node.js akan automatically install npm juga, jadi kamu cuma perlu download satu package.
Buka browser dan kunjungi website resmi Node.js di nodejs.org. Kamu akan melihat dua versi: LTS (Long Term Support) dan Current. Kalau kamu pemula, pilih LTS karena lebih stable dan akan didukung lebih lama. Download installer sesuai sistem operasi kamu, kemudian ikuti wizard instalasi. Pastikan kamu centang option untuk install npm saat proses instalasi berlangsung.
Verify instalasi Node.js dan npm.
Setelah instalasi selesai, buka terminal atau command prompt dan jalankan perintah berikut untuk verify bahwa semua berjalan dengan baik.
node --version
npm --version
Jika kedua command tersebut menampilkan versi, berarti instalasi kamu berhasil. Versi yang exact nggak terlalu penting untuk sekarang—yang penting adalah Node.js sudah terinstall di system kamu.
Membuat Project Nest.js Pertama dengan CLI
Nest.js punya command-line interface yang sangat powerful untuk generate project baru. Ini jauh lebih mudah daripada setup manual dari nol.
Install Nest CLI secara global.
Buka terminal atau command prompt kamu dan jalankan perintah ini:
npm install -g @nestjs/cli
Flag -g berarti install secara global, jadi kamu bisa pakai nest command dari mana saja di terminal. Tunggu sampai instalasi selesai. Proses ini mungkin butuh beberapa detik tergantung kecepatan internet kamu.
Verify bahwa Nest CLI terinstall.
Jalankan perintah berikut untuk pastikan CLI sudah terinstall dengan benar:
nest --version
Kalau kamu melihat nomor versi, berarti CLI sudah siap digunakan.
Buat project Nest.js baru.
Sekarang kita akan membuat project Nest.js pertama kamu. Jalankan perintah ini di terminal:
nest new bwa-nestjs-api
Pilih npm sebagai package manager ketika diminta (dibanding yarn atau pnpm).
Tunggu sampai proses selesai. Nest CLI akan create folder baru, download semua dependencies, dan setup initial project structure untuk kamu. Ini mungkin butuh beberapa menit tergantung kecepatan internet dan komputer kamu.
Explore struktur project.
Setelah selesai, masuk ke folder project kamu:
cd bwa-nestjs-api
Sekarang jalankan perintah ini untuk start development server:
npm run start:dev
Flag :dev berarti running dalam development mode dengan hot-reload. Ini artinya setiap kali kamu save file, application akan automatically restart dan reflect changes kamu. Sangat convenient untuk development.

Kalau kamu lihat pesan yang say "Nest application successfully started on port 3000", berarti server kamu sudah running dengan sempurna. Buka browser dan kunjungi http://localhost:3000 dan kamu akan lihat pesan welcome dari Nest.
Struktur folder project Nest.js.
Sekarang kita lihat struktur folder yang sudah dibuat oleh CLI. Berikut file-file penting yang perlu kamu ketahui:

Folder src berisi semua code logic aplikasi kamu. Main.ts adalah entry point aplikasi. App.module.ts adalah root module, app.controller.ts handle routing, dan app.service.ts untuk business logic. Folder test berisi test files untuk aplikasi.
File package.json berisi dependencies dan scripts seperti start, start:dev, build, dan test.
File tsconfig.json adalah konfigurasi untuk TypeScript compiler.
File nest-cli.json adalah konfigurasi khusus untuk Nest CLI yang memudahkan generate file baru dengan command.
Stop development server.
Untuk stop development server, tekan Ctrl+C di terminal.
Sekarang kamu sudah punya project Nest.js pertama yang berjalan. Di bagian selanjutnya, kita akan mulai build REST API yang sesungguhnya.
Jadi sudah siap melanjutkan? Mari kita explore fundamental concepts di Nest.js!
Arsitektur Aplikasi: Controllers, Services, Modules
Nest.js menggunakan arsitektur yang terstruktur dengan pembagian tanggung jawab yang jelas. Tiga komponen utama yang perlu kamu pahami adalah controller, service, dan module. Setiap komponen memiliki peran spesifik dalam aplikasi kamu.
Controllers adalah gerbang pertama permintaan.
Controller adalah komponen yang handle HTTP requests dari client. Tugasnya adalah menerima request, melakukan validasi awal, lalu melempar ke service untuk processing. Controller juga bertanggung jawab mengirimkan response kembali ke client. Singkatnya, controller adalah interface antara client dan logika aplikasi kamu.
Kalau kamu buat API untuk aplikasi e-commerce, controller kamu akan handle request seperti GET /products, POST /orders, PUT /users/:id, dan sebagainya. Controller nggak melakukan business logic yang kompleks—itu adalah tugas service.
Services adalah jantung dari bisnis logic.
Service adalah tempat dimana semua business logic hidup. Ini adalah komponen yang melakukan pemrosesan data, kalkulasi, validasi, dan interaksi dengan database. Service nggak tahu tentang HTTP atau request-response—ia hanya fokus pada logic aplikasi kamu.
Dengan memisahkan business logic di service, kamu membuat kode lebih reusable dan mudah di-test. Contohnya, kalau kamu punya service bernama ProductService, service ini bisa digunakan oleh multiple controllers atau bahkan di tempat lain dalam aplikasi kamu.
Modules adalah organisator dari components.
Module adalah cara Nest.js mengorganisir related components bersama-sama. Satu module bisa contain multiple controllers dan services yang saling terkait. Module memudahkan kamu untuk structure aplikasi secara logical dan membuat dependencies lebih jelas.
Pikirkan module seperti feature folder. Contohnya, kamu punya ProductModule yang contains ProductController, ProductService, dan Product model. Semua related ke product feature berada dalam satu tempat.
Hubungan antara controller, service, dan module.
Sekarang mari kita lihat bagaimana ketiganya bekerja bersama. Module adalah container. Di dalam module, kamu declare controllers dan providers (services). Controller kemudian di-inject service yang dibutuhkan melalui constructor. Ketika client mengirim request, controller menerima request tersebut, memanggil method di service, dan mengirimkan response.
Ini adalah pattern yang sangat powerful karena separation of concerns-nya jelas. Kamu bisa change business logic di service tanpa perlu touch controller. Kamu bisa test service independently tanpa perlu test HTTP layer.
Dependency Injection di Nest.js

Dependency Injection adalah salah satu fitur paling powerful di Nest.js. Meski terdengar complicated, konsepnya sebenarnya sangat simple dan akan membuat kode kamu lebih clean dan testable.
Apa itu Dependency Injection?
Dependency Injection adalah pattern dimana object nggak membuat dependencies-nya sendiri, melainkan menerima dependencies dari luar melalui constructor atau method. Ini berbeda dengan cara tradisional dimana object membuat dependencies-nya sendiri.
Contoh tanpa DI: Kalau kamu punya class ProductService yang butuh DatabaseConnection, service ini akan membuat connection-nya sendiri di constructor. Kalau kamu punya class OrderService yang juga butuh DatabaseConnection, ia juga akan membuat connection-nya sendiri. Ini inefficient dan sulit untuk test.
Contoh dengan DI: DatabaseConnection dibuat di satu tempat oleh Nest container. Ketika ProductService dan OrderService dibuat, mereka menerima DatabaseConnection yang sudah di-setup. Ini lebih efficient dan mudah untuk test karena kamu bisa inject mock DatabaseConnection.
Bagaimana DI bekerja di Nest.js?
Nest.js memiliki built-in IoC (Inversion of Control) container yang manage semua dependencies. Kamu tinggal declare apa yang service kamu butuhkan di constructor, dan Nest akan automatically provide dependencies tersebut.
Caranya sangat simple. Di constructor kamu, kamu just declare parameter dengan type yang kamu butuhkan. Nest akan see type tersebut, dan automatically inject dependency yang tepat. Ini disebut constructor injection.
Keuntungan DI di Nest.js sangat besar. Pertama, kode kamu lebih modular dan reusable. Kedua, testing menjadi jauh lebih mudah karena kamu bisa inject mock objects. Ketiga, dependency management menjadi centralized dan mudah di-manage.
Contoh praktis DI di Nest.js.
Mari kita lihat contoh sederhana. Katakanlah kamu punya UserService yang berinteraksi dengan database. Kamu ingin inject database connection ke service ini.
// user.service.ts
import { Injectable } from '@nestjs/common';
import { DatabaseService } from './database.service';
@Injectable()
export class UserService {
constructor(private readonly databaseService: DatabaseService) {}
async getUser(id: string) {
return this.databaseService.query('SELECT * FROM users WHERE id = ?', [id]);
}
}
Di sini, UserService menerima DatabaseService melalui constructor. Parameter private readonly databaseService akan automatically di-inject oleh Nest container. Kamu nggak perlu manually membuat instance DatabaseService—Nest handle semuanya.
Sekarang kalau kamu ingin test UserService, kamu bisa inject mock DatabaseService:
// user.service.spec.ts
const mockDatabaseService = {
query: jest.fn().mockResolvedValue({ id: '1', name: 'John' })
};
const userService = new UserService(mockDatabaseService as any);
const result = await userService.getUser('1');
expect(result.name).toBe('John');
Lihat betapa mudahnya? Kamu bisa test UserService tanpa butuh real database connection.
Decorators dan Bagaimana Cara Kerjanya

Decorators adalah fitur TypeScript yang membuat Nest.js code kamu very clean dan readable. Decorators adalah function yang attach metadata ke class, method, parameter, atau property.
Apa itu decorators?
Decorators di TypeScript adalah way untuk add metadata atau modify behavior dari class, method, property, atau parameter. Syntax-nya menggunakan @ symbol diikuti dengan nama decorator.
Di Nest.js, decorators adalah fundamental. Mereka digunakan untuk tell Nest.js tentang route, method HTTP apa yang digunakan, parameter apa yang diexpect, dan banyak lagi. Tanpa decorators, kamu harus write banyak boilerplate code.
Common decorators di Nest.js.
Mari kita lihat beberapa decorators yang paling sering digunakan.
@Controller decorator. Decorator ini mendefinisikan class sebagai controller dan specify base route untuk controller tersebut. Contohnya, @Controller('products') berarti semua route di controller ini akan dimulai dengan /products.
import { Controller, Get } from '@nestjs/common';
import { ProductService } from './product.service';
@Controller('products')
export class ProductController {
constructor(private readonly productService: ProductService) {}
@Get()
getAllProducts() {
return this.productService.findAll();
}
@Get(':id')
getProductById(@Param('id') id: string) {
return this.productService.findOne(id);
}
}
@Get, @Post, @Put, @Delete decorators. Decorators ini specify HTTP method untuk route tertentu. @Get menandakan route ini handle GET requests, @Post untuk POST, @Put untuk PUT, dan @Delete untuk DELETE.
@Param, @Query, @Body decorators. Decorators ini digunakan untuk extract data dari request.
- @Param digunakan untuk path parameters. Contohnya, di route GET /products/:id, :id adalah path parameter yang bisa di-extract dengan @Param('id').
- @Query digunakan untuk query strings. Contohnya, di route GET /products?page=1&limit=10, query parameters bisa di-extract dengan @Query().
- @Body digunakan untuk request body. Contohnya, di route POST /products, data dari body bisa di-extract dengan @Body().
@Post()
createProduct(@Body() createProductDto: CreateProductDto) {
return this.productService.create(createProductDto);
}
@Get(':id')
getProductById(@Param('id') id: string) {
return this.productService.findOne(id);
}
@Get()
getAllProducts(@Query() query: GetProductsQueryDto) {
return this.productService.findAll(query);
}
Bagaimana decorators bekerja di belakang layar?
Decorators di TypeScript adalah just function yang menerima target (class, method, property, atau parameter) sebagai argument dan return modified version dari target tersebut atau modify target secara langsung.
Ketika Nest.js start, ia scan semua decorators di aplikasi kamu. Setiap decorator memberi informasi kepada Nest.js tentang route apa yang ada, method HTTP apa, parameter apa yang diexpect, dan lainnya. Nest.js menggunakan informasi ini untuk build routing table.
Contohnya, ketika kamu write:
@Controller('bwa-products')
@Get(':id')
getProductById(@Param('id') id: string) {
// ...
}
Decorators ini tell Nest.js bahwa ada route GET /bwa-products/:id yang should call method getProductById dengan path parameter id.
Benefits dari decorators.
Decorators membuat code kamu very clean dan declarative. Kamu lihat what kamu define di decorator dan immediately understand apa route tersebut do. Tanpa decorators, kamu harus write banyak manual routing configuration yang membuat code kurang readable.
Selain itu, decorators membuat Nest.js bisa do banyak magic di belakang layar untuk kamu. Validation, serialization, dan banyak features lain bisa diimplement sebagai decorators yang kamu bisa gunakan.
Custom decorators untuk use cases spesifik.
Salah satu kekuatan Nest.js adalah kamu bisa buat custom decorators untuk use cases spesifik di aplikasi kamu. Misalnya, kamu bisa buat decorator @CurrentUser yang automatically extract current user dari request dan pass ke method.
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
export const CurrentUser = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.user;
}
);
Sekarang kamu bisa gunakan decorator ini di controller:
@Get('profile')
getUserProfile(@CurrentUser() user: any) {
return this.userService.getProfile(user.id);
}
Jauh lebih clean dan readable dibanding manually extract user dari request.
Jadi sudah clear bagaimana fundamental concepts di Nest.js bekerja? Controllers, services, dan modules membentuk architecture yang terstruktur. Dependency Injection membuat kode lebih modular dan testable. Decorators membuat code kamu clean dan declarative. Semua ini bekerja bersama untuk membuat Nest.js sangat powerful dan ergonomic.
Di bagian berikutnya, kita akan start praktik dan membuat REST API pertama kamu dengan mengaplikasikan semua concepts ini. Mari kita mulai build sesuatu yang real!
Setup Controller Sederhana
Sekarang sudah waktunya untuk buat sesuatu yang nyata. Kita akan membuat controller pertama yang akan handle HTTP requests dari client.
Langkah pertama adalah generate controller baru menggunakan Nest CLI. Buka terminal dan jalankan perintah ini:
nest generate controller bwa-product
Atau gunakan shorthand:
nest g co bwa-product
Perintah ini akan create dua file: bwa-product.controller.ts dan bwa-product.controller.spec.ts. Buka file bwa-product.controller.ts dan kamu akan lihat struktur dasar:
// src/bwa-product/bwa-product.controller.ts
import { Controller } from '@nestjs/common';
@Controller('bwa-product')
export class BwaProductController {}
Controller sudah di-decorate dengan @Controller('bwa-product') yang specify base route. Semua endpoint di controller ini akan dimulai dengan /bwa-product.
Sekarang buat service dengan perintah:
nest g s bwa-product
Ini akan create bwa-product.service.ts. Service ini akan berisi logic untuk fetch dan manage product data. Buka file tersebut dan buat method sederhana:
// src/bwa-product/bwa-product.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class BwaProductService {
private products = [
{ id: 1, name: 'Laptop', price: 10000000 },
{ id: 2, name: 'Mouse', price: 200000 },
{ id: 3, name: 'Keyboard', price: 500000 }
];
getAllProducts() {
return this.products;
}
}
Sekarang inject service ke controller:
// src/bwa-product/bwa-product.controller.ts
import { Controller, Get } from '@nestjs/common';
import { BwaProductService } from './bwa-product.service';
@Controller('bwa-product')
export class BwaProductController {
constructor(private readonly bwaProductService: BwaProductService) {}
@Get()
getAllProducts() {
return this.bwaProductService.getAllProducts();
}
}
Perhatikan bahwa kita inject BwaProductService melalui constructor. Nest akan automatically provide instance-nya. Method getAllProducts memiliki decorator @Get() yang berarti method ini handle GET request ke /bwa-product.
Sekarang daftar controller dan service di app.module.ts:
// src/app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { BwaProductController } from './bwa-product/bwa-product.controller';
import { BwaProductService } from './bwa-product/bwa-product.service';
@Module({
imports: [],
controllers: [AppController, BwaProductController],
providers: [AppService, BwaProductService],
})
export class AppModule {}
Sekarang endpoint sudah registered dan siap digunakan.
Membuat GET Endpoint
Sekarang mari kita buat GET endpoint dengan berbagai variasi. Yang pertama adalah endpoint untuk get all products—ini sudah ada di atas. Sekarang tambah method untuk get special offer products:
import { Controller, Get } from '@nestjs/common';
import { BwaProductService } from './bwa-product.service';
@Controller('bwa-product')
export class BwaProductController {
constructor(private readonly bwaProductService: BwaProductService) {}
@Get()
getAllProducts() {
return this.bwaProductService.getAllProducts();
}
@Get('special-offer')
getSpecialOfferProducts() {
return this.bwaProductService.getSpecialOfferProducts();
}
}
Method kedua ini memiliki decorator @Get('special-offer') yang berarti route ini handle GET request ke /bwa-product/special-offer.
Penting untuk notice bahwa kita put getSpecialOfferProducts sebelum method yang handle dynamic route. Kenapa? Karena Nest.js process routes dalam order mereka dideklarasikan. Kalau kita put dynamic route dulu, /bwa-product/special-offer akan match sebagai parameter :id.
Sekarang tambah method baru ke service:
// src/bwa-product/bwa-product.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class BwaProductService {
private products = [
{ id: 1, name: 'Laptop', price: 10000000, specialOffer: true },
{ id: 2, name: 'Mouse', price: 200000, specialOffer: false },
{ id: 3, name: 'Keyboard', price: 500000, specialOffer: true }
];
getAllProducts() {
return this.products;
}
getSpecialOfferProducts() {
return this.products.filter(product => product.specialOffer === true);
}
getProductById(id: number) {
return this.products.find(product => product.id === id);
}
}
Sekarang service punya dua method tambahan. getSpecialOfferProducts akan filter products dengan specialOffer true. getProductById akan find product berdasarkan ID.
Menambahkan Route Parameter
Route parameter adalah bagian dari URL yang bersifat dynamic. Contohnya, /bwa-product/1 dimana 1 adalah ID product yang kita request. Di Nest.js, route parameter di-define dengan syntax :namaParameter.
Mari kita add method untuk get product by ID:
// src/bwa-product/bwa-product.controller.ts
import { Controller, Get, Param } from '@nestjs/common';
import { BwaProductService } from './bwa-product.service';
@Controller('bwa-product')
export class BwaProductController {
constructor(private readonly bwaProductService: BwaProductService) {}
@Get()
getAllProducts() {
return this.bwaProductService.getAllProducts();
}
@Get('special-offer')
getSpecialOfferProducts() {
return this.bwaProductService.getSpecialOfferProducts();
}
@Get(':id')
getProductById(@Param('id') id: string) {
const productId = parseInt(id, 10);
return this.bwaProductService.getProductById(productId);
}
}
Parameter :id adalah route parameter. Method getProductById menerima parameter id melalui @Param('id') decorator. Kita convert string id menjadi number menggunakan parseInt.
Important note tentang route ordering.
Kalau kamu punya multiple routes di controller yang sama, urutan sangat penting. Route yang lebih specific harus didefinisikan sebelum route yang lebih generic. Contohnya:
// src/bwa-product/bwa-product.controller.ts
@Controller('bwa-product')
export class BwaProductController {
@Get('special-offer') // Specific route
getSpecialOfferProducts() { ... }
@Get(':id') // Generic route dengan parameter
getProductById(@Param('id') id: string) { ... }
}
Kalau urutan dibalik, /bwa-product/special-offer akan ditangkap oleh route :id dengan id = 'special-offer', bukan di special-offer route. Ini adalah common mistake yang perlu diingat.
Multiple route parameters.
Kadang kamu perlu multiple parameters di satu route. Contohnya, GET /bwa-product/:productId/reviews/:reviewId untuk get specific review dari specific product:
// src/bwa-product/bwa-product.controller.ts
@Get(':productId/reviews/:reviewId')
getProductReview(
@Param('productId') productId: string,
@Param('reviewId') reviewId: string
) {
const pId = parseInt(productId, 10);
const rId = parseInt(reviewId, 10);
return this.bwaProductService.getProductReview(pId, rId);
}
Kamu bisa define berapa pun parameters yang dibutuhkan. Setiap parameter di-extract dengan @Param() decorator.
Type casting untuk parameters.
Parameter dari URL selalu string, jadi kamu perlu cast ke tipe data yang diinginkan. Kamu bisa gunakan ParseIntPipe untuk automatically parse string menjadi number:
// src/bwa-product/bwa-product.controller.ts
import { Controller, Get, Param, ParseIntPipe } from '@nestjs/common';
@Controller('bwa-product')
export class BwaProductController {
@Get(':id')
getProductById(@Param('id', ParseIntPipe) id: number) {
return this.bwaProductService.getProductById(id);
}
}
Dengan ParseIntPipe, Nest akan automatically parse string id menjadi number. Kalau conversion gagal, Nest akan return 400 Bad Request error. Ini lebih clean dan safe daripada manual parsing.
Sekarang kamu sudah tahu cara membuat GET endpoint dengan berbagai variasi. Di bagian berikutnya, kita akan explore POST endpoint untuk create data baru. Akan lebih kompleks tapi sangat powerful!
Test Endpoint
Setelah kamu buat endpoint, tentu kamu ingin test apakah endpoint tersebut berfungsi dengan baik. Paling simple, kamu bisa buka browser dan akses URL endpoint langsung. Di sini saya menggunakan HTTPie.

Memisahkan Logika Bisnis di dalam Service
Service adalah tempat di mana semua logika bisnis hidup. Tugasnya adalah handle data processing, validasi, dan business rules. Controller hanya perlu delegate ke service dan return response. Pemisahan ini adalah core principle dari clean architecture.
Kenapa perlu pisahin? Karena dengan memisahkan, kamu membuat kode lebih testable, reusable, dan mudah dimaintain. Service bisa digunakan oleh multiple controllers atau bahkan di tempat lain dalam aplikasi kamu. Kalau logika bisnis tercampur dengan HTTP layer di controller, kamu akan duplicate code dan logic menjadi harder to maintain.
Mari kita lihat contoh praktis. Sebelumnya, service kita hanya berisi dummy data. Sekarang mari kita buat service yang lebih realistic dengan business logic yang actual.
// src/bwa-product/bwa-product.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class BwaProductService {
private products = [
{ id: 1, name: 'Laptop', price: 10000000, stock: 5, specialOffer: true },
{ id: 2, name: 'Mouse', price: 200000, stock: 50, specialOffer: false },
{ id: 3, name: 'Keyboard', price: 500000, stock: 20, specialOffer: true }
];
getAllProducts() {
return this.products;
}
getProductById(id: number) {
return this.products.find(product => product.id === id);
}
getSpecialOfferProducts() {
return this.products.filter(product => product.specialOffer === true);
}
getProductsInStock() {
return this.products.filter(product => product.stock > 0);
}
calculateTotalPrice(productId: number, quantity: number) {
const product = this.getProductById(productId);
if (!product) {
throw new Error('Product tidak ditemukan');
}
if (product.stock < quantity) {
throw new Error('Stock tidak cukup');
}
let totalPrice = product.price * quantity;
if (product.specialOffer) {
totalPrice = totalPrice * 0.9; // Diskon 10%
}
return {
productId: product.id,
productName: product.name,
quantity,
unitPrice: product.price,
totalPrice,
discount: product.specialOffer ? '10%' : 'Tidak ada'
};
}
}
Sekarang service punya method calculateTotalPrice yang handle business logic kompleks. Method ini:
- Cek apakah product ada
- Validasi stock tersedia
- Hitung total price
- Apply diskon kalau special offer
- Return detailed pricing information
Semua logic ini berada di service, bukan di controller. Controller hanya perlu call method ini dan return hasilnya.
// src/bwa-product/bwa-product.controller.ts
import { Controller, Get, Param, ParseIntPipe, Query } from '@nestjs/common';
import { BwaProductService } from './bwa-product.service';
@Controller('bwa-product')
export class BwaProductController {
constructor(private readonly bwaProductService: BwaProductService) {}
@Get()
getAllProducts() {
return this.bwaProductService.getAllProducts();
}
@Get('special-offer')
getSpecialOfferProducts() {
return this.bwaProductService.getSpecialOfferProducts();
}
@Get('in-stock')
getProductsInStock() {
return this.bwaProductService.getProductsInStock();
}
@Get('calculate-price')
calculatePrice(@Query('productId', ParseIntPipe) productId: number, @Query('quantity', ParseIntPipe) quantity: number) {
return this.bwaProductService.calculateTotalPrice(productId, quantity);
}
@Get(':id')
getProductById(@Param('id', ParseIntPipe) id: number) {
return this.bwaProductService.getProductById(id);
}
}
Lihat bagaimana controller menjadi sangat clean dan simple? Tidak ada business logic sama sekali. Controller hanya menerima request, call appropriate method di service, dan return response.

Praktik Terbaik dalam Menulis Service
Ada beberapa best practices yang harus kamu follow ketika menulis service. Ini akan membuat service kamu lebih maintainable dan scalable.
Satu tanggung jawab per service.
Setiap service harus punya single responsibility. Jangan buat service yang handle multiple unrelated features. Contohnya, jangan buat service yang handle product dan order. Buat ProductService dan OrderService terpisah.
Kalau service kamu mengerjakan terlalu banyak hal, ia akan sulit di-test dan sulit dimaintain. Principle ini disebut Single Responsibility Principle (SRP) dari SOLID principles.
Gunakan descriptive names.
Nama method harus descriptive dan jelas tentang apa yang ia lakukan. Avoid generic names seperti process, execute, atau handle. Gunakan nama yang specific seperti calculateTotalPrice, validateProductStock, atau applyDiscount.
// BURUK - nama terlalu generic
calculatePrice(id, qty) { }
process() { }
handle() { }
// BAIK - nama descriptive
calculateTotalPriceWithDiscount(productId, quantity) { }
validateProductStockAvailability(productId, quantity) { }
applySpecialOfferDiscount(price) { }
Validasi input di service.
Meskipun controller bisa do basic validation, service harus juga validasi input untuk business logic-nya. Ini memastikan data yang masuk sudah valid sebelum diproses.
// src/bwa-product/bwa-product.service.ts
calculateTotalPrice(productId: number, quantity: number) {
// Validasi input
if (!productId || productId <= 0) {
throw new Error('Product ID harus lebih besar dari 0');
}
if (!quantity || quantity <= 0) {
throw new Error('Quantity harus lebih besar dari 0');
}
const product = this.getProductById(productId);
if (!product) {
throw new Error('Product tidak ditemukan');
}
if (product.stock < quantity) {
throw new Error('Stock tidak cukup');
}
// Logic lainnya...
}
Return simple data structures.
Service harus return data yang simple dan mudah dipahami. Avoid returning complex nested objects. Return hanya data yang dibutuhkan oleh caller.
// BURUK - return complex nested structure
{
product: {
details: {
info: {
id: 1
}
}
}
}
// BAIK - return flat structure
{
productId: 1,
productName: 'Laptop',
totalPrice: 9000000
}
Gunakan dependency injection untuk dependencies.
Kalau service butuh interact dengan database, cache, atau service lain, inject mereka melalui constructor. Jangan create instance manual.
// BURUK - manual instantiation
export class BwaProductService {
private database = new DatabaseService();
}
// BAIK - dependency injection
export class BwaProductService {
constructor(private readonly database: DatabaseService) {}
}
Throw meaningful errors.
Ketika ada error, throw error yang meaningful dengan message yang jelas. Ini memudahkan debugging dan client akan tahu apa yang salah.
// BURUK
if (!product) throw new Error('Error');
// BAIK
if (!product) throw new Error('Product dengan ID ' + productId + ' tidak ditemukan');
Testing Sederhana untuk Service
Service adalah komponen yang paling important untuk di-test karena berisi business logic. Testing service juga paling simple karena tidak perlu HTTP mocking.
Mari kita buat test sederhana untuk BwaProductService. Nest sudah punya built-in testing utilities. Penting untuk remember bahwa testing di Nest.js menggunakan Jest dan dependency injection yang sama seperti di application code.
Untuk test service, kita cukup create module dengan service di providers. Untuk test controller, kita perlu add service ke providers juga agar Nest bisa inject-nya dengan benar.
// src/bwa-product/bwa-product.service.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { BwaProductService } from './bwa-product.service';
describe('BwaProductService', () => {
let service: BwaProductService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [BwaProductService],
}).compile();
service = module.get<BwaProductService>(BwaProductService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
it('should return all products', () => {
const products = service.getAllProducts();
expect(products.length).toBeGreaterThan(0);
});
it('should return product by id', () => {
const product = service.getProductById(1);
expect(product).toBeDefined();
expect(product.id).toBe(1);
expect(product.name).toBe('Laptop');
});
it('should return special offer products', () => {
const specialProducts = service.getSpecialOfferProducts();
expect(specialProducts.every(p => p.specialOffer === true)).toBe(true);
});
it('should calculate total price with discount', () => {
const result = service.calculateTotalPrice(1, 2);
expect(result.productId).toBe(1);
expect(result.quantity).toBe(2);
expect(result.discount).toBe('10%');
});
it('should throw error when product not found', () => {
expect(() => {
service.calculateTotalPrice(999, 1);
}).toThrow('Product tidak ditemukan');
});
it('should throw error when stock insufficient', () => {
expect(() => {
service.calculateTotalPrice(2, 1000);
}).toThrow('Stock tidak cukup');
});
});
Jika kamu ingin test controller juga, pastikan untuk add service ke module:
// src/bwa-product/bwa-product.controller.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { BwaProductController } from './bwa-product.controller';
import { BwaProductService } from './bwa-product.service';
describe('BwaProductController', () => {
let controller: BwaProductController;
let service: BwaProductService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [BwaProductController],
providers: [BwaProductService],
}).compile();
controller = module.get<BwaProductController>(BwaProductController);
service = module.get<BwaProductService>(BwaProductService);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
it('should return all products', () => {
const result = controller.getAllProducts();
expect(result.length).toBeGreaterThan(0);
});
it('should return product by id', () => {
const result = controller.getProductById(1);
expect(result).toBeDefined();
expect(result.id).toBe(1);
});
});
Sekarang mari kita break down test ini:
beforeEach hook. Ini run sebelum setiap test. Kita create module dan get service instance. Ini memastikan setiap test punya fresh service instance.
Test methods. Setiap test case memiliki descriptive name yang jelas tentang apa yang di-test. Kita test:
- Service dapat di-instantiate
- getAllProducts return data
- getProductById return correct product
- getSpecialOfferProducts hanya return special products
- calculateTotalPrice calculate dengan benar dan apply diskon
- Error di-throw ketika product tidak ditemukan
- Error di-throw ketika stock tidak cukup
Assertions. Kita gunakan expect untuk assert hasil yang diharapkan. Kalau actual result tidak match dengan expected result, test akan fail.
Untuk run test, jalankan perintah:
npm run test
Atau untuk run test dengan watch mode (auto-rerun ketika file berubah):
npm run test:watch

Kalau semua test pass, kamu akan lihat output yang menunjukkan berapa test yang run dan berapa yang pass.
Best practices untuk testing service:
- Test happy path (scenario yang berfungsi normal)
- Test edge cases dan error conditions
- Test dengan berbagai input untuk memastikan robustness
- Keep test simple dan focused pada satu behavior
- Use descriptive test names
Service testing ini adalah foundation untuk membuat aplikasi yang reliable. Invest waktu untuk write good tests di awal akan save kamu banyak waktu debugging di kemudian hari.
Jadi sekarang kamu sudah tahu cara membuat service dengan business logic yang proper, follow best practices, dan test service kamu. Di bagian berikutnya, kita akan explore handling berbagai HTTP methods dan request bodies. Akan sangat powerful!
Struktur Folder yang Rapi
Struktur folder yang baik adalah fondasi untuk project yang mudah dimaintain. Gunakan organisasi berbasis fitur dimana setiap fitur punya folder tersendiri:
bwa-nestjs-api/
├── src/
│ ├── bwa-product/
│ │ ├── bwa-product.controller.ts
│ │ ├── bwa-product.service.ts
│ │ ├── dto/
│ │ │ ├── create-product.dto.ts
│ │ │ └── update-product.dto.ts
│ │ ├── entities/
│ │ │ └── product.entity.ts
│ │ └── bwa-product.module.ts
│ ├── bwa-order/
│ ├── common/
│ │ ├── exceptions/
│ │ ├── decorators/
│ │ └── pipes/
│ ├── config/
│ ├── app.module.ts
│ └── main.ts
├── test/
├── package.json
└── tsconfig.json
Kenapa berbasis fitur? Setiap fitur independen dan mudah diperluas. Tidak seperti berbasis layer (folder controllers, services terpisah) yang lebih sulit dinavigasi ketika project besar.
Folder DTO. Data Transfer Object untuk validasi masukan request. Gunakan class-validator untuk mendefinisikan aturan validasi:
// src/bwa-product/dto/create-product.dto.ts
import { IsString, IsNumber, Min } from 'class-validator';
export class CreateProductDto {
@IsString()
name: string;
@IsNumber()
@Min(1000)
price: number;
}
Folder entities. Struktur data untuk representasi database.
Folder common. Utilitas bersama seperti exceptions, decorators, pipes yang digunakan di banyak tempat.
Folder config. Semua konfigurasi aplikasi dalam satu tempat agar mudah dikelola.
Best Practices Nest.js
1. Selalu gunakan DTOs untuk validasi masukan.
DTOs tidak hanya untuk struktur data, tetapi juga untuk validasi. Nest akan secara otomatis menolak request yang tidak valid dengan kode 400 Bad Request.
2. Gunakan kode status HTTP yang bermakna.
- 200 OK untuk GET, PUT
- 201 Created untuk POST
- 204 No Content untuk DELETE
- 400 Bad Request untuk masukan tidak valid
- 401 Unauthorized, 403 Forbidden, 404 Not Found
Gunakan dekorator @HttpCode:
@Post()
@HttpCode(201)
create(@Body() createProductDto: CreateProductDto) {
return this.bwaProductService.create(createProductDto);
}
3. Kembalikan format response yang konsisten.
Buat antarmuka respons dasar agar semua endpoint mengembalikan format yang sama:
export interface ApiResponse<T> {
success: boolean;
data?: T;
error?: string;
}
4. Atur imports dengan rapi.
Urutan imports: library eksternal → Nest.js → imports aplikasi → imports relatif.
5. Gunakan logger untuk melacak peristiwa.
import { Logger, Injectable } from '@nestjs/common';
@Injectable()
export class BwaProductService {
private readonly logger = new Logger(BwaProductService.name);
getProductById(id: number) {
this.logger.log(`Mengambil produk ID: ${id}`);
return product;
}
}
6. Gunakan variabel lingkungan untuk konfigurasi.
Jangan hardcode konfigurasi. Gunakan file .env:
DATABASE_URL=postgresql://localhost:5432/bwa_db
JWT_SECRET=your-secret
NODE_ENV=development
Akses dengan process.env.DATABASE_URL.
7. Menangani kesalahan dengan anggun menggunakan filter exception.
Buat filter exception untuk menangani semua exception dengan format response yang konsisten.
Selamat! Kamu sudah menyelesaikan panduan Nest.js untuk pemula. Kamu sudah mempelajari:
- Fundamental arsitektur Nest.js (controllers, services, modules)
- Setup project dan membuat endpoint
- Pemisahan logika bisnis dengan services
- Best practices untuk aplikasi yang dapat diskalakan
Sekarang terapkan pengetahuan ini dengan membuat project nyata. Mulai dari yang kecil, iterasikan, dan secara bertahap tambahkan kompleksitas. Semoga panduan ini membantu perjalanan kamu menjadi developer Nest.js yang ahli!
Penutup
Selamat! Kamu sudah menyelesaikan "Panduan Lengkap Nest.js untuk Pemula". Dalam panduan ini, kamu telah mempelajari fundamental Nest.js mulai dari pengenalan framework, setup project, konsep arsitektur, membuat endpoint pertama, business logic dengan services, hingga best practices.
Kamu sudah punya fondasi yang kuat untuk memulai membangun REST API dengan Nest.js. Ingat bahwa cara terbaik untuk belajar adalah dengan praktek. Jangan hanya membaca—mulai menulis kode dan bangun project nyata.
Lanjutkan Belajar di BuildWithAngga
Untuk memperdalam pengetahuan Nest.js kamu, BuildWithAngga menyediakan kursus lengkap tentang backend development yang mencakup arsitektur advanced, integrasi database, autentikasi, keamanan, dan pembangunan project nyata. Bergabung dengan komunitas BuildWithAngga juga akan memberikan kamu akses ke bimbingan mentor, umpan balik, dan peluang karir yang menarik.
Langkah Berikutnya
Mulai membangun projects sekarang untuk mempraktikkan konsep fundamental yang sudah kamu pelajari. Bergabunglah dengan BuildWithAngga untuk mendapatkan akses ke resources, bimbingan mentor, dan komunitas developer. Tingkatkan keterampilan secara bertahap dengan membangun lebih banyak projects dengan kompleksitas yang meningkat, dan terus eksplorasi fitur Nest.js yang lebih advanced.
Ingat: perjalanan menjadi developer Nest.js yang expert dimulai dengan satu langkah pertama. Mulai menulis kode sekarang dan nikmati prosesnya!