File size: 2,437 Bytes
c51c0db | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | import { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';
import { Prisma } from '@prisma/client';
import { logger } from '../logger';
export interface ApiErrorResponse {
error: string;
message: string;
code?: string;
details?: any;
}
export const setupErrorHandler = (server: FastifyInstance) => {
server.setErrorHandler((error: any, request: FastifyRequest, reply: FastifyReply) => {
// Log the error
logger.error({
err: error,
path: request.url,
method: request.method,
requestId: request.id
}, 'API Error detected');
// 1. Prisma Errors mapping
if (error instanceof Prisma.PrismaClientKnownRequestError) {
// P2002: Unique constraint failed
if (error.code === 'P2002') {
return reply.code(409).send({
error: 'Conflict',
message: `A record with this ${error.meta?.target} already exists.`,
code: 'PRISMA_P2002'
});
}
// P2025: Record not found
if (error.code === 'P2025') {
return reply.code(404).send({
error: 'Not Found',
message: 'The requested record does not exist.',
code: 'PRISMA_P2025'
});
}
}
// 2. Validation Errors (Fastify/Ajv)
if (error.validation) {
return reply.code(400).send({
error: 'Bad Request',
message: 'Validation failed',
details: error.validation,
code: 'VALIDATION_ERROR'
});
}
// 3. JWT/Auth Errors
if (error.statusCode === 401 || error.code === 'FST_JWT_NO_AUTHORIZATION_TOKEN') {
return reply.code(401).send({
error: 'Unauthorized',
message: error.message || 'Authentication required',
code: 'AUTH_ERROR'
});
}
// 4. Fallback for generic errors
const statusCode = error.statusCode || 500;
return reply.code(statusCode).send({
error: statusCode === 500 ? 'Internal Server Error' : error.name || 'Error',
message: statusCode === 500 ? 'An unexpected error occurred' : error.message,
code: error.code || 'GENERIC_ERROR'
});
});
};
|