วันนี้เราจะมาสร้าง TimeoutInterceptor
ที่กำหนดเวลาหมดอายุของคำขอ(request) ใน NestJS โดยใช้ค่าเวลาที่กำหนดจากตัวแปร (environment variable). การใช้ nestjs-cls จะช่วยให้เราสามารถควบคุมการทำงานของ TimeoutInterceptor ได้ตามความต้องการ
เตรียมสภาพแวดล้อม
ก่อนอื่นคุณต้องติดตั้งแพ็กเกจที่จำเป็น:
npm install @nestjs/common @nestjs/core rxjs nestjs-cls
กำหนดตัวแปรสภาพแวดล้อม
สร้างไฟล์ .env และกำหนดค่าของตัวแปรเวลาหมดอายุ:
GATEWAY_TIMEOUT=30000
ตัวแปรนี้จะถูกใช้เพื่อกำหนดเวลาหมดอายุของคำขอใน TimeoutInterceptor.
สร้าง TimeoutInterceptor
สร้างไฟล์ timeout.interceptor.ts และเพิ่มโค้ดต่อไปนี้
import {
CallHandler,
ExecutionContext,
GatewayTimeoutException,
Injectable,
NestInterceptor,
} from '@nestjs/common';
import { ClsService } from 'nestjs-cls';
import {
catchError,
Observable,
throwError,
timeout,
TimeoutError,
} from 'rxjs';
@Injectable()
export class TimeoutInterceptor implements NestInterceptor {
constructor(
private readonly timeoutInMillis: number,
private readonly clsService: ClsService,
) {}
intercept(_: ExecutionContext, next: CallHandler): Observable<any> {
// ตรวจสอบค่า 'skipTimeout' ใน ClsService
if (this.clsService.get('skipTimeout')) return next.handle();
// ใช้ timeout และจัดการข้อผิดพลาด
return next.handle().pipe(
timeout(this.timeoutInMillis),
catchError((err) => {
if (err instanceof TimeoutError) {
throw new GatewayTimeoutException({
errorCode: 'GATEWAY_TIMEOUT',
error: 'Gateway Timeout',
message: 'Gateway timeout has occurred',
});
}
return throwError(() => err);
}),
);
}
}
อธิบาย
- timeout(this.timeoutInMillis): กำหนดเวลาให้กับคำขอ และทำให้คำขอหมดอายุถ้าใช้เวลานานเกินไป
- catchError((err) => { ... }): จัดการข้อผิดพลาดที่เกิดจากการหมดอายุของคำขอ (TimeoutError)
กำหนด Interceptor ใน AppModule
ในไฟล์ app.module.ts ของคุณ
import { Module } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { ClsModule, ClsService } from 'nestjs-cls';
import { TimeoutInterceptor } from './shared/interceptor/timeout.interceptor';
import { APP_INTERCEPTOR } from '@nestjs/core';
@Module({
imports: [
ClsModule.forRoot({
global: true, // ตั้งค่าให้เป็น global module
middleware: {
mount: true, // ใช้งาน middleware สำหรับการสร้าง context
},
}),
],
providers: [
{
provide: APP_INTERCEPTOR,
useFactory: (configService: ConfigService, clsService: ClsService) => {
const gatewayTimeout = configService.get<string>('GATEWAY_TIMEOUT');
const timeout = gatewayTimeout ? parseInt(gatewayTimeout, 10) : 30000;
return new TimeoutInterceptor(timeout, clsService);
},
inject: [ConfigService, ClsService],
},
],
})
export class AppModule {}
อธิบาย
- useFactory: ใช้ฟังก์ชันเพื่อสร้างอินสแตนซ์ของ TimeoutInterceptor โดยการอ่านค่าจากตัวแปรสภาพแวดล้อม และตรวจสอบค่าด้วย parseInt
- inject: ทำให้ NestJS สามารถจัดการการฉีด (inject) ของ ConfigService และ ClsService ได้
สรุป
- ClsModule.forRoot: การกำหนดค่า global module และ middleware สำหรับ nestjs-cls ช่วยให้คุณสามารถจัดการ context สำหรับคำขอได้อย่างง่าย
- Middleware: ช่วยในการสร้างและจัดการ context ให้กับคำขอแต่ละคำขอใน app
- Interceptor: ใช้ ClsService เพื่อตรวจสอบค่าจาก context และจัดการ timeout