Files
MinglarBackendNestJS/src/common/utils/helper/ApiError.ts
paritosh18 82cbaddce1 feat: add serverless-offline plugin and new showSuggestion function
- Added serverless-offline plugin to package.json and serverless.yml for local development.
- Implemented new showSuggestion function in host.yml with appropriate memory size and event configuration.
- Removed deprecated getSuggestion function from minglaradmin.yml and updated related handlers.
- Enhanced safeHandler to convert Prisma errors to ApiError automatically, improving error handling.
- Introduced comprehensive Prisma error handling in ApiError.ts, mapping error codes to user-friendly messages and HTTP status codes.
2025-11-28 15:33:43 +05:30

265 lines
8.7 KiB
TypeScript

import { Prisma } from '@prisma/client';
/**
* Prisma error code mappings to user-friendly messages and HTTP status codes
*/
const PRISMA_ERROR_CODES: Record<string, { statusCode: number; message: string }> = {
// Common errors (P1xxx)
P1000: { statusCode: 500, message: 'Authentication failed against database server' },
P1001: { statusCode: 503, message: 'Database server is not reachable' },
P1002: { statusCode: 504, message: 'Database server timed out' },
P1003: { statusCode: 500, message: 'Database does not exist' },
P1008: { statusCode: 504, message: 'Database operation timed out' },
P1009: { statusCode: 409, message: 'Database already exists' },
P1010: { statusCode: 403, message: 'User was denied access to the database' },
P1011: { statusCode: 500, message: 'Error opening a TLS connection' },
P1012: { statusCode: 500, message: 'Schema validation error' },
P1013: { statusCode: 400, message: 'Invalid database connection string' },
P1014: { statusCode: 500, message: 'Underlying model does not exist' },
P1015: { statusCode: 500, message: 'Database schema uses unsupported features' },
P1016: { statusCode: 400, message: 'Raw query has incorrect number of parameters' },
P1017: { statusCode: 503, message: 'Database server has closed the connection' },
// Prisma Client Query Engine errors (P2xxx)
P2000: { statusCode: 400, message: 'Value too long for column' },
P2001: { statusCode: 404, message: 'Record not found' },
P2002: { statusCode: 409, message: 'Unique constraint violation' },
P2003: { statusCode: 409, message: 'Foreign key constraint violation' },
P2004: { statusCode: 400, message: 'Database constraint violation' },
P2005: { statusCode: 400, message: 'Invalid value stored in database' },
P2006: { statusCode: 400, message: 'Invalid value provided' },
P2007: { statusCode: 400, message: 'Data validation error' },
P2008: { statusCode: 400, message: 'Failed to parse the query' },
P2009: { statusCode: 400, message: 'Failed to validate the query' },
P2010: { statusCode: 500, message: 'Raw query failed' },
P2011: { statusCode: 400, message: 'Null constraint violation' },
P2012: { statusCode: 400, message: 'Missing required value' },
P2013: { statusCode: 400, message: 'Missing required argument' },
P2014: { statusCode: 409, message: 'Required relation violation' },
P2015: { statusCode: 404, message: 'Related record not found' },
P2016: { statusCode: 400, message: 'Query interpretation error' },
P2017: { statusCode: 400, message: 'Records for relation not connected' },
P2018: { statusCode: 404, message: 'Required connected records not found' },
P2019: { statusCode: 400, message: 'Input error' },
P2020: { statusCode: 400, message: 'Value out of range' },
P2021: { statusCode: 500, message: 'Table does not exist' },
P2022: { statusCode: 500, message: 'Column does not exist' },
P2023: { statusCode: 500, message: 'Inconsistent column data' },
P2024: { statusCode: 503, message: 'Connection pool timeout' },
P2025: { statusCode: 404, message: 'Record not found' },
P2026: { statusCode: 400, message: 'Unsupported database feature used in query' },
P2027: { statusCode: 500, message: 'Multiple database errors occurred' },
P2028: { statusCode: 500, message: 'Transaction API error' },
P2029: { statusCode: 400, message: 'Query parameter limit exceeded' },
P2030: { statusCode: 400, message: 'Fulltext index not found' },
P2031: { statusCode: 500, message: 'MongoDB requires replica set' },
P2033: { statusCode: 400, message: 'Number does not fit in 64 bit signed integer' },
P2034: { statusCode: 409, message: 'Transaction failed due to write conflict or deadlock' },
P2035: { statusCode: 500, message: 'Database assertion violation' },
P2036: { statusCode: 500, message: 'External connector error' },
P2037: { statusCode: 503, message: 'Too many database connections opened' },
};
interface PrismaErrorMeta {
target?: string[];
field_name?: string;
model_name?: string;
argument_name?: string;
constraint?: string;
cause?: string;
[key: string]: unknown;
}
class ApiError<T = unknown> extends Error {
statusCode: number;
data: T | null;
message: string;
success: boolean;
errors: Array<Error>;
isOperational: boolean;
stack?: string;
code?: string;
meta?: PrismaErrorMeta;
constructor(
statusCode: number,
message: string = 'Something went wrong',
errors: Array<Error> = [],
isOperational: boolean = true,
stack?: string,
code?: string,
meta?: PrismaErrorMeta
) {
super(message);
this.statusCode = statusCode;
this.data = null;
this.message = message;
this.success = false;
this.errors = errors;
this.isOperational = isOperational;
this.code = code;
this.meta = meta;
if (stack) {
this.stack = stack;
} else {
Error.captureStackTrace(this, this.constructor);
}
}
/**
* Creates an ApiError from a Prisma error
* Handles all Prisma error types: PrismaClientKnownRequestError, PrismaClientUnknownRequestError,
* PrismaClientRustPanicError, PrismaClientInitializationError, PrismaClientValidationError
*/
static fromPrismaError(error: unknown): ApiError {
// Handle PrismaClientKnownRequestError
if (error instanceof Prisma.PrismaClientKnownRequestError) {
const errorInfo = PRISMA_ERROR_CODES[error.code] || {
statusCode: 500,
message: 'Database operation failed',
};
let message = errorInfo.message;
const meta = error.meta as PrismaErrorMeta | undefined;
// Provide more specific messages based on error code and meta
switch (error.code) {
case 'P2002': {
const target = meta?.target;
if (target && Array.isArray(target)) {
message = `Unique constraint violation on field(s): ${target.join(', ')}`;
}
break;
}
case 'P2003': {
const fieldName = meta?.field_name;
if (fieldName) {
message = `Foreign key constraint failed on field: ${fieldName}`;
}
break;
}
case 'P2025': {
const cause = meta?.cause;
if (cause) {
message = `Record not found: ${cause}`;
}
break;
}
case 'P2011': {
const constraint = meta?.constraint;
if (constraint) {
message = `Null constraint violation on: ${constraint}`;
}
break;
}
case 'P2014': {
const modelName = meta?.model_name;
if (modelName) {
message = `Required relation violation on model: ${modelName}`;
}
break;
}
}
return new ApiError(
errorInfo.statusCode,
message,
[error],
true,
error.stack,
error.code,
meta
);
}
// Handle PrismaClientUnknownRequestError
if (error instanceof Prisma.PrismaClientUnknownRequestError) {
return new ApiError(
500,
'An unknown database error occurred',
[error],
true,
error.stack,
'UNKNOWN_REQUEST_ERROR'
);
}
// Handle PrismaClientRustPanicError
if (error instanceof Prisma.PrismaClientRustPanicError) {
return new ApiError(
500,
'A critical database error occurred. Please try again later.',
[error],
false,
error.stack,
'RUST_PANIC_ERROR'
);
}
// Handle PrismaClientInitializationError
if (error instanceof Prisma.PrismaClientInitializationError) {
const errorInfo = error.errorCode
? PRISMA_ERROR_CODES[error.errorCode] || { statusCode: 500, message: 'Database initialization failed' }
: { statusCode: 500, message: 'Database initialization failed' };
return new ApiError(
errorInfo.statusCode,
errorInfo.message,
[error],
false,
error.stack,
error.errorCode || 'INITIALIZATION_ERROR'
);
}
// Handle PrismaClientValidationError
if (error instanceof Prisma.PrismaClientValidationError) {
return new ApiError(
400,
'Invalid data provided for database operation',
[error],
true,
error.stack,
'VALIDATION_ERROR'
);
}
// Fallback for any other error
if (error instanceof Error) {
return new ApiError(500, error.message, [error], true, error.stack);
}
return new ApiError(500, 'An unexpected error occurred');
}
/**
* Check if an error is a Prisma error
*/
static isPrismaError(error: unknown): boolean {
return (
error instanceof Prisma.PrismaClientKnownRequestError ||
error instanceof Prisma.PrismaClientUnknownRequestError ||
error instanceof Prisma.PrismaClientRustPanicError ||
error instanceof Prisma.PrismaClientInitializationError ||
error instanceof Prisma.PrismaClientValidationError
);
}
/**
* Get a user-friendly message for a Prisma error code
*/
static getPrismaErrorMessage(code: string): string {
return PRISMA_ERROR_CODES[code]?.message || 'Database operation failed';
}
/**
* Get HTTP status code for a Prisma error code
*/
static getPrismaErrorStatusCode(code: string): number {
return PRISMA_ERROR_CODES[code]?.statusCode || 500;
}
}
export default ApiError;