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'
        });
    });
};