การกำหนด Timeout ของ Request ใน NestJS

Sept. 7, 2024 · boychawin

วันนี้เราจะมาสร้าง 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

Documentation