From 7c4c83e5d828ca2da220a5cb1a8c1867fabbee4c Mon Sep 17 00:00:00 2001 From: realrafi Date: Wed, 22 Jul 2026 14:41:18 +0330 Subject: [PATCH] feat: added response interceptor and exception filter --- src/common/filters/all-exception.filter.ts | 42 +++++++++++++++++++ .../interceptors/response.interceptor.ts | 32 ++++++++++++++ .../interfaces/api-response.interface.ts | 7 ++++ src/main.ts | 18 +++++--- 4 files changed, 94 insertions(+), 5 deletions(-) create mode 100644 src/common/filters/all-exception.filter.ts create mode 100644 src/common/interceptors/response.interceptor.ts create mode 100644 src/common/interfaces/api-response.interface.ts diff --git a/src/common/filters/all-exception.filter.ts b/src/common/filters/all-exception.filter.ts new file mode 100644 index 0000000..d9a9123 --- /dev/null +++ b/src/common/filters/all-exception.filter.ts @@ -0,0 +1,42 @@ +import { ArgumentsHost, Catch, ExceptionFilter, HttpException, HttpStatus, Logger } from "@nestjs/common"; +import { Request, Response } from "express"; + +@Catch() +export class AllExceptionFilter implements ExceptionFilter { + private readonly logger = new Logger(AllExceptionFilter.name); + + catch(exception: unknown, host: ArgumentsHost): void { + const ctx = host.switchToHttp(); + + const response = ctx.getResponse(); + const request = ctx.getRequest(); + + const status = exception instanceof HttpException + ? exception.getStatus() + : HttpStatus.INTERNAL_SERVER_ERROR + + let message: string | string[] = 'خطای داخلی سرور!'; + + if(exception instanceof HttpException) { + const exceptionResponse = exception.getResponse(); + + if(typeof exceptionResponse === 'string') { + message = exceptionResponse; + } else if(typeof exceptionResponse === 'object' && exceptionResponse !== null) { + message = (exceptionResponse as {message?: string | string[]}).message ?? message; + } + } + + if(status === HttpStatus.INTERNAL_SERVER_ERROR) { + this.logger.error(exception); + } + + response.status(status).json({ + success: false, + statusCode: status, + message, + path: request.url, + timestamp: new Date().toISOString() + }) + } +} \ No newline at end of file diff --git a/src/common/interceptors/response.interceptor.ts b/src/common/interceptors/response.interceptor.ts new file mode 100644 index 0000000..ea954b6 --- /dev/null +++ b/src/common/interceptors/response.interceptor.ts @@ -0,0 +1,32 @@ +import { + CallHandler, + ExecutionContext, + Injectable, + NestInterceptor, +} from '@nestjs/common'; +import { ApiResponse } from '../interfaces/api-response.interface'; +import { map, Observable } from 'rxjs'; +import { Response } from 'express'; + +@Injectable() +export class ResponseInterceptor implements NestInterceptor< + T, + ApiResponse +> { + intercept( + context: ExecutionContext, + next: CallHandler, + ): Observable> { + const response = context.switchToHttp().getResponse(); + + return next.handle().pipe( + map((data: T) => ({ + success: true, + statusCode: response.statusCode, + message: 'درخواست با موفقیت پاسخ داده شد!', + timestamp: new Date().toISOString(), + data + })) + ); + } +} diff --git a/src/common/interfaces/api-response.interface.ts b/src/common/interfaces/api-response.interface.ts new file mode 100644 index 0000000..da7db19 --- /dev/null +++ b/src/common/interfaces/api-response.interface.ts @@ -0,0 +1,7 @@ +export interface ApiResponse { + success: boolean; + statusCode: number; + message: string; + timestamp: string; + data: T; +} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 633b475..e646119 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,12 +1,18 @@ import { NestFactory, Reflector } from '@nestjs/core'; import { AppModule } from './app.module'; -import { ClassSerializerInterceptor, Logger, ValidationPipe } from '@nestjs/common'; +import { + ClassSerializerInterceptor, + Logger, + ValidationPipe, +} from '@nestjs/common'; import { setupSwagger } from './config/swagger.config'; import { ConfigService } from '@nestjs/config'; +import { AllExceptionFilter } from './common/filters/all-exception.filter'; +import { ResponseInterceptor } from './common/interceptors/response.interceptor'; async function bootstrap() { const logger = new Logger('APP'); - + const app = await NestFactory.create(AppModule); app.useGlobalPipes( @@ -16,14 +22,16 @@ async function bootstrap() { transform: true, }), ); - - app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector))); + app.useGlobalInterceptors( + new ResponseInterceptor(), + new ClassSerializerInterceptor(app.get(Reflector)), + ); + app.useGlobalFilters(new AllExceptionFilter()); setupSwagger(app); const configService = app.get(ConfigService); - const PORT = configService.getOrThrow('APP_PORT'); await app.listen(PORT, '0.0.0.0', () => { logger.log(`Server running at http://localhost:${PORT}`);