Wawancara NestJS: Guards, Interceptors, dan arsitektur modular
Pertanyaan umum dalam wawancara teknis NestJS tentang Guards, Interceptors, dan arsitektur modular dengan contoh kode TypeScript konkret dan penjelasan teknis.

Wawancara teknis NestJS secara konsisten berfokus pada tiga pilar framework: Guards, Interceptors, dan arsitektur modular. Mekanisme ini menjadi tulang punggung setiap aplikasi NestJS yang terstruktur dengan baik, dan perekrut menggunakannya untuk menilai penguasaan framework yang sesungguhnya.
Guard mengontrol akses (siapa yang masuk), Interceptor mengubah data (apa yang masuk dan keluar), sementara arsitektur modular mengatur semuanya. Menguasai ketiganya menunjukkan pemahaman NestJS yang mendalam, melampaui operasi CRUD dasar.
Cara kerja Guards NestJS dan skenario wawancara umum
Guards mengimplementasikan antarmuka CanActivate dan menentukan apakah sebuah request mencapai handler. Berbeda dengan middleware, Guards mengakses execution context NestJS (ExecutionContext), yang memungkinkan keputusan otorisasi berdasarkan metadata handler.
Pertanyaan klasik: "Implementasikan Guard yang memeriksa peran pengguna."
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
// Retrieve roles defined via the @Roles() decorator
const requiredRoles = this.reflector.getAllAndOverride<string[]>('roles', [
context.getHandler(),
context.getClass(),
]);
// No roles required means the route is public
if (!requiredRoles) return true;
// Extract user from the HTTP request
const { user } = context.switchToHttp().getRequest();
// Check if the user holds at least one required role
return requiredRoles.some((role) => user.roles?.includes(role));
}
}Reflector membaca metadata yang dilampirkan oleh decorator kustom. Decorator @Roles() yang sesuai terlihat seperti ini:
import { SetMetadata } from '@nestjs/common';
// Creates a decorator that attaches roles as metadata
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);Jawaban yang kuat dalam wawancara melampaui kode. Urutan eksekusi penting: Middleware → Guards → Interceptors (sebelum) → Pipes → Handler → Interceptors (sesudah) → Exception Filters. Urutan ini muncul di hampir setiap wawancara NestJS.
Interceptors NestJS: mengubah request dan response
Interceptors mengimplementasikan NestInterceptor dan menggunakan RxJS untuk memanipulasi aliran data sebelum dan sesudah handler. Kekuatannya berasal dari akses ke CallHandler, yang merepresentasikan pipeline eksekusi.
Pertanyaan yang sering muncul: "Buat sebuah Interceptor logging yang mengukur waktu eksekusi."
import { Injectable, NestInterceptor, ExecutionContext, CallHandler, Logger } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
private readonly logger = new Logger(LoggingInterceptor.name);
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const request = context.switchToHttp().getRequest();
const { method, url } = request;
const now = Date.now();
// next.handle() invokes the route handler
return next.handle().pipe(
tap(() => {
// Runs AFTER the handler responds
const duration = Date.now() - now;
this.logger.log(`${method} ${url} — ${duration}ms`);
}),
);
}
}Operator RxJS tap mengamati stream tanpa mengubahnya. Untuk mengubah response, operator yang tepat adalah map:
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
// Generic interface for a standardized API response
interface ApiResponse<T> {
data: T;
timestamp: string;
path: string;
}
@Injectable()
export class TransformInterceptor<T> implements NestInterceptor<T, ApiResponse<T>> {
intercept(context: ExecutionContext, next: CallHandler): Observable<ApiResponse<T>> {
const request = context.switchToHttp().getRequest();
return next.handle().pipe(
map((data) => ({
data,
timestamp: new Date().toISOString(),
path: request.url,
})),
);
}
}Pola response standar ini memberi nilai bagus dalam wawancara: menunjukkan kemampuan berpikir tentang desain API dan konsistensi response.
Middleware: pemrosesan HTTP mentah (seperti Express). Guard: keputusan boolean untuk akses. Interceptor: transformasi aliran request/response dengan RxJS. Mencampurkan ketiga konsep ini dalam wawancara adalah red flag instan.
Arsitektur modular NestJS: membangun aplikasi yang skalabel
Arsitektur modular memisahkan developer junior dari senior dalam wawancara. NestJS memaksakan organisasi berbasis modul, tetapi pertanyaan sebenarnya menyasar keputusan arsitektur: kapan membuat modul dan bagaimana mengelola dependensi antar modul.
Pertanyaan tingkat lanjut: "Bagaimana Anda menstruktur aplikasi e-commerce NestJS dengan modul yang dipisahkan dengan baik?"
import { Module } from '@nestjs/common';
import { OrdersService } from './orders.service';
import { OrdersController } from './orders.controller';
import { PaymentsModule } from '../payments/payments.module';
import { ProductsModule } from '../products/products.module';
@Module({
// Import modules that OrdersModule depends on
imports: [PaymentsModule, ProductsModule],
controllers: [OrdersController],
providers: [OrdersService],
// Expose OrdersService to modules that import OrdersModule
exports: [OrdersService],
})
export class OrdersModule {}Jebakan klasik wawancara: dependensi siklik. Jika OrdersModule mengimpor PaymentsModule dan PaymentsModule mengimpor OrdersModule, NestJS gagal saat startup. Jalan keluar daruratnya menggunakan forwardRef:
// Resolving circular dependencies
@Module({
imports: [forwardRef(() => PaymentsModule)],
})
export class OrdersModule {}Jawaban paling kuat menjelaskan bahwa forwardRef adalah jalan pintas, bukan solusi sebenarnya. Perbaikan yang tepat adalah merefaktor untuk mengekstrak modul bersama (SharedOrderPaymentModule) atau menggunakan Event Emitter untuk memisahkan komunikasi.
Siap menguasai wawancara Node.js / NestJS Anda?
Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.
Dynamic Modules dan konfigurasi tingkat lanjut
Dynamic Modules menciptakan modul yang dapat dikonfigurasi dan digunakan ulang antar proyek. Pola ini muncul dalam wawancara level senior.
Pertanyaan: "Buat sebuah modul cache yang dapat dikonfigurasi dengan opsi dinamis."
import { Module, DynamicModule, Global } from '@nestjs/common';
import { CacheService } from './cache.service';
// Module configuration interface
export interface CacheModuleOptions {
ttl: number; // Time-to-live in seconds
maxItems: number; // Maximum number of entries
prefix: string; // Key prefix
}
@Global() // Available everywhere without explicit import
@Module({})
export class CacheModule {
static forRoot(options: CacheModuleOptions): DynamicModule {
return {
module: CacheModule,
providers: [
{
// Inject options via a custom token
provide: 'CACHE_OPTIONS',
useValue: options,
},
CacheService,
],
exports: [CacheService],
};
}
}Service yang sesuai menyuntikkan opsi melalui @Inject():
import { Injectable, Inject } from '@nestjs/common';
import { CacheModuleOptions } from './cache.module';
@Injectable()
export class CacheService {
private store = new Map<string, { value: any; expiry: number }>();
constructor(@Inject('CACHE_OPTIONS') private options: CacheModuleOptions) {}
set(key: string, value: any): void {
const prefixedKey = `${this.options.prefix}:${key}`;
this.store.set(prefixedKey, {
value,
expiry: Date.now() + this.options.ttl * 1000,
});
}
get<T>(key: string): T | null {
const prefixedKey = `${this.options.prefix}:${key}`;
const entry = this.store.get(prefixedKey);
// Check expiration before returning
if (!entry || entry.expiry < Date.now()) return null;
return entry.value as T;
}
}Pola forRoot / forRootAsync adalah klasik NestJS. forRootAsync memuat konfigurasi dari ConfigService yang dapat di-inject, yang menjadi praktik produksi yang direkomendasikan.
Menandai modul sebagai @Global() tampak praktis, tetapi penyalahgunaannya menciptakan kopling yang tidak terlihat. Menyebutkan dalam wawancara bahwa @Global() harus dicadangkan untuk service lintas-aplikasi (config, cache, logger) menunjukkan kematangan arsitektur.
Custom Decorators dan komposisi Guards
Custom decorators menggabungkan beberapa Guards dan metadata dalam satu anotasi tunggal. Topik ini muncul untuk posisi mid-senior.
import { applyDecorators, UseGuards, SetMetadata } from '@nestjs/common';
import { JwtAuthGuard } from './jwt-auth.guard';
import { RolesGuard } from './roles.guard';
// Combines JWT authentication + role verification
export function Auth(...roles: string[]) {
return applyDecorators(
SetMetadata('roles', roles),
UseGuards(JwtAuthGuard, RolesGuard),
);
}Penggunaan dalam controller:
import { Controller, Get, Post, Body } from '@nestjs/common';
import { Auth } from '../auth/auth.decorator';
import { OrdersService } from './orders.service';
@Controller('orders')
export class OrdersController {
constructor(private ordersService: OrdersService) {}
@Get()
@Auth('admin', 'manager') // Single decorator replaces UseGuards + Roles
findAll() {
return this.ordersService.findAll();
}
@Post()
@Auth('admin')
create(@Body() dto: CreateOrderDto) {
return this.ordersService.create(dto);
}
}Pola applyDecorators menyederhanakan kode controller dan memusatkan logika otorisasi. Mengusulkan pendekatan ini secara proaktif dalam wawancara adalah sinyal yang kuat.
Kesimpulan
- Guards menggunakan
CanActivatedanReflectoruntuk keputusan akses berdasarkan metadata handler - Interceptors memanfaatkan RxJS (
tap,map) untuk mengubah aliran sebelum dan sesudah handler - Urutan eksekusi Middleware → Guards → Interceptors → Pipes → Handler adalah pertanyaan yang hampir universal
- Dependensi siklik harus diselesaikan melalui refactoring arsitektur, bukan dengan
forwardRef - Dynamic Modules (
forRoot/forRootAsync) menunjukkan kemampuan membangun komponen yang dapat digunakan kembali applyDecoratorsmenggabungkan beberapa Guards menjadi satu decorator yang mudah dibaca- Latihlah pertanyaan-pertanyaan ini dengan kode nyata pada modul NestJS dan Interceptors
Mulai berlatih!
Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.
Tag
Bagikan
Artikel terkait

NestJS + Prisma: stack backend modern untuk Node.js
Panduan lengkap untuk membangun API backend modern dengan NestJS dan Prisma. Setup, model, service, transaksi, dan praktik terbaik dijelaskan.

Pertanyaan Wawancara Backend Node.js: Panduan Lengkap 2026
25 pertanyaan wawancara backend Node.js yang paling sering ditanyakan. Event loop, async/await, streams, clustering, dan performa dijelaskan dengan jawaban terperinci.

NestJS: Membangun REST API Lengkap dari Nol
Panduan lengkap membangun REST API profesional dengan NestJS. Controller, Service, Module, validasi dengan class-validator, dan penanganan error dijelaskan secara praktis.