feat: initial commit
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
import { ArgumentsHost, Catch, ExceptionFilter, HttpStatus, Logger } from '@nestjs/common';
|
||||
import { Response } from 'express';
|
||||
import { Prisma } from '@prisma/client';
|
||||
|
||||
/**
|
||||
* Without this, an unmigrated table (P2021), a bad foreign key (P2003), etc.
|
||||
* all surfaced as an opaque "Internal Server Error" with no indication of
|
||||
* the actual cause — this maps the common Prisma error codes to sensible
|
||||
* HTTP statuses and echoes the real reason back (safe in dev; consider
|
||||
* stripping detail in production).
|
||||
*/
|
||||
@Catch(Prisma.PrismaClientKnownRequestError, Prisma.PrismaClientValidationError)
|
||||
export class PrismaExceptionFilter implements ExceptionFilter {
|
||||
private readonly logger = new Logger('PrismaExceptionFilter');
|
||||
|
||||
catch(exception: Prisma.PrismaClientKnownRequestError | Prisma.PrismaClientValidationError, host: ArgumentsHost) {
|
||||
const ctx = host.switchToHttp();
|
||||
const response = ctx.getResponse<Response>();
|
||||
|
||||
this.logger.error(exception.message);
|
||||
|
||||
if (exception instanceof Prisma.PrismaClientValidationError) {
|
||||
return response.status(HttpStatus.BAD_REQUEST).json({
|
||||
statusCode: HttpStatus.BAD_REQUEST,
|
||||
error: 'Invalid request shape for the database query',
|
||||
message: exception.message.split('\n').pop()?.trim(),
|
||||
});
|
||||
}
|
||||
|
||||
switch (exception.code) {
|
||||
case 'P2021': // table does not exist — schema not pushed/migrated
|
||||
return response.status(HttpStatus.SERVICE_UNAVAILABLE).json({
|
||||
statusCode: HttpStatus.SERVICE_UNAVAILABLE,
|
||||
error: 'Database schema not initialized',
|
||||
message: `${exception.meta?.table ?? 'A required table'} does not exist. Run "pnpm prisma:push" (or "pnpm prisma:migrate") against this DATABASE_URL.`,
|
||||
});
|
||||
case 'P2025': // record not found for update/delete
|
||||
return response.status(HttpStatus.NOT_FOUND).json({
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
error: 'Not found',
|
||||
message: exception.meta?.cause ?? 'Record not found',
|
||||
});
|
||||
case 'P2002': // unique constraint violation
|
||||
return response.status(HttpStatus.CONFLICT).json({
|
||||
statusCode: HttpStatus.CONFLICT,
|
||||
error: 'Conflict',
|
||||
message: `Unique constraint failed on: ${(exception.meta?.target as string[] | undefined)?.join(', ')}`,
|
||||
});
|
||||
case 'P2003': // foreign key constraint violation
|
||||
return response.status(HttpStatus.BAD_REQUEST).json({
|
||||
statusCode: HttpStatus.BAD_REQUEST,
|
||||
error: 'Invalid reference',
|
||||
message: `Foreign key constraint failed on: ${exception.meta?.field_name}`,
|
||||
});
|
||||
default:
|
||||
return response.status(HttpStatus.INTERNAL_SERVER_ERROR).json({
|
||||
statusCode: HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
error: 'Database error',
|
||||
message: `[${exception.code}] ${exception.message.split('\n').pop()?.trim()}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Global, Module } from '@nestjs/common';
|
||||
import { PrismaService } from './prisma.service';
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
providers: [PrismaService],
|
||||
exports: [PrismaService],
|
||||
})
|
||||
export class PrismaModule {}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
|
||||
@Injectable()
|
||||
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
|
||||
async onModuleInit() {
|
||||
await this.$connect();
|
||||
}
|
||||
|
||||
async onModuleDestroy() {
|
||||
await this.$disconnect();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user