66 HttpStatus ,
77} from '@nestjs/common' ;
88import { Request , Response } from 'express' ;
9+ import { AppError } from '../exceptions/app-error' ;
10+ import { ErrorCodes } from '../exceptions/error-codes.enum' ;
911
1012@Catch ( )
1113export class GlobalExceptionFilter implements ExceptionFilter {
@@ -15,31 +17,72 @@ export class GlobalExceptionFilter implements ExceptionFilter {
1517 const request = ctx . getRequest < Request > ( ) ;
1618
1719 let status = HttpStatus . INTERNAL_SERVER_ERROR ;
18- let errorResponse : any = {
20+ let payload : Record < string , unknown > = {
1921 statusCode : status ,
2022 error : 'InternalServerError' ,
2123 message : 'An unexpected error occurred' ,
24+ code : ErrorCodes . GENERIC ,
2225 path : request . url ,
2326 timestamp : new Date ( ) . toISOString ( ) ,
2427 } ;
2528
26- if ( exception instanceof HttpException ) {
29+ if ( exception instanceof AppError ) {
30+ // AppError already structures message/code/details
31+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
32+ const res : any = exception . getResponse ( ) ;
2733 status = exception . getStatus ( ) ;
28- const res = exception . getResponse ( ) ;
29-
30- errorResponse = {
34+ payload = {
35+ statusCode : status ,
36+ error : res . error ?? exception . name ,
37+ message : res . message ?? exception . message ,
38+ code : res . code ?? ( exception as AppError ) . code ,
39+ details : res . details ?? ( exception as AppError ) . details ,
40+ path : request . url ,
41+ timestamp : new Date ( ) . toISOString ( ) ,
42+ } ;
43+ } else if ( exception instanceof HttpException ) {
44+ status = exception . getStatus ( ) ;
45+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
46+ const res : any = exception . getResponse ( ) ;
47+ payload = {
3148 statusCode : status ,
32- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
33- error : res [ 'error' ] ?? exception . name ,
34- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
35- message : res [ 'message' ] ?? exception . message ,
36- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
37- details : res [ 'details' ] ,
49+ error : res . error ?? exception . name ,
50+ message : res . message ?? exception . message ,
51+ code : res . code ?? ErrorCodes . GENERIC ,
52+ details : res . details ,
3853 path : request . url ,
3954 timestamp : new Date ( ) . toISOString ( ) ,
4055 } ;
4156 }
4257
43- response . status ( status ) . json ( errorResponse ) ;
58+ // Structured error logging for observability / tracking
59+ try {
60+ const logPayload = {
61+ level : 'error' ,
62+ message :
63+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
64+ ( payload [ 'message' ] as unknown ) ?? 'Unhandled exception' ,
65+ error : exception instanceof Error ? exception . stack : exception ,
66+ request : {
67+ method : request . method ,
68+ url : request . url ,
69+ params : request . params ,
70+ query : request . query ,
71+ } ,
72+ code : payload [ 'code' ] ,
73+ timestamp : new Date ( ) . toISOString ( ) ,
74+ } ;
75+
76+ // Replace with an external tracker (Sentry, Datadog) integration here
77+ // For now, print structured JSON to console so logs can be parsed.
78+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
79+ console . error ( JSON . stringify ( logPayload ) ) ;
80+ } catch ( logErr ) {
81+ // If logging fails, still respond to client
82+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
83+ console . error ( 'Failed to log error' , logErr ) ;
84+ }
85+
86+ response . status ( status ) . json ( payload ) ;
4487 }
4588}
0 commit comments