สัมภาษณ์ NestJS: Guards, Interceptors และสถาปัตยกรรมแบบโมดูล
คำถามที่พบบ่อยในการสัมภาษณ์เทคนิค NestJS เกี่ยวกับ Guards, Interceptors และสถาปัตยกรรมแบบโมดูล พร้อมตัวอย่างโค้ด TypeScript ที่เป็นรูปธรรมและคำอธิบายทางเทคนิค

การสัมภาษณ์เทคนิค NestJS มักเน้นไปที่สามเสาหลักของเฟรมเวิร์กอย่างสม่ำเสมอ ได้แก่ Guards, Interceptors และสถาปัตยกรรมแบบโมดูล กลไกเหล่านี้เป็นกระดูกสันหลังของแอปพลิเคชัน NestJS ที่ออกแบบดี และผู้คัดเลือกใช้สิ่งเหล่านี้เพื่อประเมินความเชี่ยวชาญที่แท้จริงในเฟรมเวิร์ก
Guard ควบคุมการเข้าถึง (ใครเข้าได้) Interceptor แปลงข้อมูล (อะไรเข้าและออก) ส่วนสถาปัตยกรรมแบบโมดูลจัดระเบียบทุกอย่าง การเชี่ยวชาญทั้งสามอย่างแสดงถึงความเข้าใจ NestJS ที่ลึกซึ้งเกินกว่าการดำเนินการ CRUD พื้นฐาน
Guards ใน NestJS ทำงานอย่างไรและสถานการณ์สัมภาษณ์ที่พบบ่อย
Guards implement อินเทอร์เฟซ CanActivate และตัดสินว่าคำขอจะไปถึง handler หรือไม่ ต่างจาก middleware ตรงที่ Guards เข้าถึง execution context ของ NestJS (ExecutionContext) ซึ่งทำให้สามารถตัดสินใจเรื่องการอนุญาตโดยอิงตาม metadata ของ handler
คำถามคลาสสิก: "จงเขียน Guard ที่ตรวจสอบบทบาทของผู้ใช้"
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 อ่าน metadata ที่ถูกแนบโดย custom decorator ส่วน decorator @Roles() ที่จับคู่กันมีลักษณะดังนี้:
import { SetMetadata } from '@nestjs/common';
// Creates a decorator that attaches roles as metadata
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);คำตอบที่หนักแน่นในการสัมภาษณ์ต้องมากกว่าการเขียนโค้ด ลำดับการทำงานสำคัญ: Middleware → Guards → Interceptors (ก่อน) → Pipes → Handler → Interceptors (หลัง) → Exception Filters ลำดับนี้ปรากฏในการสัมภาษณ์ NestJS แทบทุกครั้ง
Interceptors ของ NestJS: การแปลง request และ response
Interceptors implement NestInterceptor และใช้ RxJS ในการจัดการสตรีมข้อมูลก่อนและหลัง handler ความสามารถหลักมาจากการเข้าถึง CallHandler ซึ่งเป็นตัวแทนของ pipeline การทำงาน
คำถามที่พบบ่อย: "จงสร้าง logging Interceptor ที่วัดเวลาทำงาน"
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`);
}),
);
}
}ตัวดำเนินการ tap ของ RxJS สังเกตสตรีมโดยไม่แก้ไข หากต้องการแปลง response ตัวดำเนินการที่เหมาะสมคือ 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,
})),
);
}
}รูปแบบ response ที่เป็นมาตรฐานนี้ได้คะแนนดีในการสัมภาษณ์: แสดงถึงความสามารถในการคิดเรื่องการออกแบบ API และความสอดคล้องของ response
Middleware: การประมวลผล HTTP ดิบ (เหมือน Express) Guard: การตัดสินใจเข้าถึงแบบ boolean Interceptor: การแปลงสตรีม request/response ด้วย RxJS การสับสนระหว่างสามแนวคิดนี้ในการสัมภาษณ์เป็นสัญญาณเตือนทันที
สถาปัตยกรรมแบบโมดูลของ NestJS: สร้างแอปพลิเคชันที่ขยายขนาดได้
สถาปัตยกรรมแบบโมดูลแยกนักพัฒนาระดับ junior ออกจาก senior ในการสัมภาษณ์ NestJS บังคับการจัดระเบียบตามโมดูล แต่คำถามจริงพุ่งเป้าไปที่การตัดสินใจเชิงสถาปัตยกรรม: เมื่อไหร่ควรสร้างโมดูลและจัดการการพึ่งพาระหว่างโมดูลอย่างไร
คำถามขั้นสูง: "คุณจะวางโครงสร้างแอปพลิเคชัน e-commerce ใน NestJS ที่มีโมดูลแยกกันอย่างเหมาะสมอย่างไร"
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 {}กับดักคลาสสิกของการสัมภาษณ์: การพึ่งพาแบบวงกลม หาก OrdersModule import PaymentsModule และ PaymentsModule import OrdersModule NestJS จะล้มเหลวตอนเริ่มทำงาน ทางออกฉุกเฉินคือใช้ forwardRef:
// Resolving circular dependencies
@Module({
imports: [forwardRef(() => PaymentsModule)],
})
export class OrdersModule {}คำตอบที่หนักแน่นที่สุดอธิบายว่า forwardRef เป็นการแก้ปัญหาเฉพาะหน้า ไม่ใช่การแก้ที่แท้จริง การแก้ไขที่ถูกต้องคือการ refactor เพื่อแยกโมดูลที่ใช้ร่วมกัน (SharedOrderPaymentModule) หรือใช้ Event Emitter เพื่อแยกการสื่อสารออกจากกัน
พร้อมที่จะพิชิตการสัมภาษณ์ Node.js / NestJS แล้วหรือยังครับ?
ฝึกฝนด้วยตัวจำลองแบบโต้ตอบ, flashcards และแบบทดสอบเทคนิคครับ
Dynamic Modules และการกำหนดค่าขั้นสูง
Dynamic Modules สร้างโมดูลที่กำหนดค่าได้และนำกลับมาใช้ใหม่ระหว่างโครงการ รูปแบบนี้ปรากฏในการสัมภาษณ์ระดับ senior
คำถาม: "จงสร้างโมดูล cache ที่กำหนดค่าได้พร้อมตัวเลือกแบบไดนามิก"
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],
};
}
}บริการที่สอดคล้องกันจะ inject ตัวเลือกผ่าน @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;
}
}รูปแบบ forRoot / forRootAsync เป็นคลาสสิกของ NestJS โดย forRootAsync โหลดการกำหนดค่าจาก ConfigService ที่ inject ได้ ซึ่งเป็นแนวปฏิบัติที่แนะนำสำหรับ production
การทำเครื่องหมายโมดูลเป็น @Global() ดูสะดวก แต่การใช้มากเกินไปสร้างการเชื่อมโยงที่มองไม่เห็น การกล่าวในการสัมภาษณ์ว่า @Global() ควรสงวนไว้สำหรับบริการที่ตัดข้าม (config, cache, logger) แสดงถึงวุฒิภาวะเชิงสถาปัตยกรรม
Custom Decorators และการประกอบ Guards
Custom decorators รวม Guards หลายตัวและ metadata ไว้ใน annotation เดียว หัวข้อนี้ปรากฏในตำแหน่ง 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),
);
}การใช้งานใน 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);
}
}รูปแบบ applyDecorators ทำให้โค้ด controller เรียบง่ายขึ้นและรวมศูนย์ตรรกะการอนุญาต การเสนอแนวทางนี้อย่างเป็นเชิงรุกในการสัมภาษณ์เป็นสัญญาณที่หนักแน่น
บทสรุป
- Guards ใช้
CanActivateและReflectorในการตัดสินใจเข้าถึงตาม metadata ของ handler - Interceptors ใช้ประโยชน์จาก RxJS (
tap,map) เพื่อแปลงสตรีมก่อนและหลัง handler - ลำดับการทำงาน Middleware → Guards → Interceptors → Pipes → Handler เป็นคำถามแทบจะสากล
- การพึ่งพาแบบวงกลมควรแก้ไขด้วยการ refactor เชิงสถาปัตยกรรม ไม่ใช่ด้วย
forwardRef - Dynamic Modules (
forRoot/forRootAsync) แสดงถึงความสามารถในการสร้างคอมโพเนนต์ที่นำกลับมาใช้ใหม่ได้ applyDecoratorsรวม Guards หลายตัวเข้าเป็น decorator เดียวที่อ่านง่าย- ฝึกคำถามเหล่านี้ด้วยโค้ดจริงใน โมดูล NestJS และ Interceptors
เริ่มฝึกซ้อมเลย!
ทดสอบความรู้ของคุณด้วยตัวจำลองสัมภาษณ์และแบบทดสอบเทคนิคครับ
แท็ก
แชร์
บทความที่เกี่ยวข้อง

NestJS + Prisma: สแตกแบ็กเอนด์สมัยใหม่สำหรับ Node.js
คู่มือฉบับสมบูรณ์ในการสร้าง API แบ็กเอนด์สมัยใหม่ด้วย NestJS และ Prisma ครอบคลุมการตั้งค่า โมเดล เซอร์วิส ทรานแซกชัน และแนวปฏิบัติที่ดี

คำถามสัมภาษณ์ Backend Node.js: คู่มือฉบับสมบูรณ์ 2026
25 คำถามสัมภาษณ์ Backend Node.js ที่พบบ่อยที่สุด Event loop, async/await, streams, clustering และประสิทธิภาพอธิบายพร้อมคำตอบโดยละเอียด

NestJS: สร้าง REST API ที่สมบูรณ์ตั้งแต่เริ่มต้น
คู่มือฉบับสมบูรณ์สำหรับการสร้าง REST API ระดับมืออาชีพด้วย NestJS ครอบคลุม Controller, Service, Module, การตรวจสอบข้อมูลด้วย class-validator และการจัดการข้อผิดพลาด