feat: added response interceptor and exception filter

This commit is contained in:
2026-07-22 14:41:18 +03:30
parent d9494da48f
commit 7c4c83e5d8
4 changed files with 94 additions and 5 deletions
@@ -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<Response>();
const request = ctx.getRequest<Request>();
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()
})
}
}
@@ -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<T> implements NestInterceptor<
T,
ApiResponse<T>
> {
intercept(
context: ExecutionContext,
next: CallHandler,
): Observable<ApiResponse<T>> {
const response = context.switchToHttp().getResponse<Response>();
return next.handle().pipe(
map((data: T) => ({
success: true,
statusCode: response.statusCode,
message: 'درخواست با موفقیت پاسخ داده شد!',
timestamp: new Date().toISOString(),
data
}))
);
}
}
@@ -0,0 +1,7 @@
export interface ApiResponse<T> {
success: boolean;
statusCode: number;
message: string;
timestamp: string;
data: T;
}
+13 -5
View File
@@ -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>(ConfigService);
const PORT = configService.getOrThrow<number>('APP_PORT');
await app.listen(PORT, '0.0.0.0', () => {
logger.log(`Server running at http://localhost:${PORT}`);