Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions apps/api/src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { APP_GUARD } from '@nestjs/core';
import { ScheduleModule } from '@nestjs/schedule';
import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler';
import { AdminModule } from './admin/admin.module';
import { AuthModule } from './auth/auth.module';
import { DisputesModule } from './disputes/disputes.module';
Expand All @@ -17,6 +19,10 @@ import { ReviewsModule } from './reviews/reviews.module';
imports: [
ConfigModule.forRoot({ isGlobal: true }),
ScheduleModule.forRoot(),
ThrottlerModule.forRoot([{
ttl: 60000,
limit: 100,
}]),
PrismaModule,
RedisModule,
AuthModule,
Expand All @@ -30,5 +36,11 @@ import { ReviewsModule } from './reviews/reviews.module';
AdminModule,
ReviewsModule,
],
providers: [
{
provide: APP_GUARD,
useClass: ThrottlerGuard,
},
],
})
export class AppModule {}
56 changes: 56 additions & 0 deletions apps/api/src/common/filters/http-exception.filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import {
ExceptionFilter,
Catch,
ArgumentsHost,
HttpException,
HttpStatus,
} from '@nestjs/common';
import { Request, Response } from 'express';
import { Prisma } from '@prisma/client';

@Catch()
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();

let status = HttpStatus.INTERNAL_SERVER_ERROR;
let message: string | string[] = 'Internal server error';
let error = 'Internal Server Error';

// 1. Handle Standard NestJS HTTP Exceptions (like Validation errors)
if (exception instanceof HttpException) {
status = exception.getStatus();
const exceptionResponse: any = exception.getResponse();

// ValidationPipe returns an array of messages, others return a string
message = exceptionResponse.message || exception.message;
error = exceptionResponse.error || exception.name;
}
// 2. Handle Prisma Database Errors
else if (exception instanceof Prisma.PrismaClientKnownRequestError) {
if (exception.code === 'P2002') {
status = HttpStatus.CONFLICT;
const target = (exception.meta?.target as string[])?.join(', ') || 'field';
message = `Unique constraint failed on ${target}. This record already exists.`;
error = 'Conflict';
}
}
// 3. Fallback for unhandled native Javascript errors
else if (exception instanceof Error) {
message = exception.message;
}

// Standardize the message to always be an array for frontend consistency
const formattedMessage = Array.isArray(message) ? message : [message];

response.status(status).json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
message: formattedMessage,
error: error,
});
}
}
2 changes: 2 additions & 0 deletions apps/api/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { HttpExceptionFilter } from './common/filters/http-exception.filter';

async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalFilters(new HttpExceptionFilter());
app.useGlobalPipes(
new ValidationPipe({ whitelist: true, transform: true, forbidNonWhitelisted: true }),
);
Expand Down
Loading
Loading