Oleksii Shevchuk
Oleksii Shevchuk

Reputation: 71

NestJS: Best Practice of exception handling (Exception filters vs Interceptors.catchError)

What are pros and cons using Exception filters over Interceptors.catchError . (Exception mapping)?

Upvotes: 7

Views: 3759

Answers (1)

vijayakumarpsg587
vijayakumarpsg587

Reputation: 1177

Not sure if you have already have the answer, but as far as my implementation goes, the following scenarios use either filters or interceptors

Exception Filters:

exception filters are a mechanism to capture the errors thrown from the app/ be it when the modules are loaded or request being initiated, they are the best case scenario to capture the issues especially runtime errors.

Consider a scenario where the custom logger service you have implemented, (say a pino logger service), has a redact path like this: (note: loggerService should have defined the implemented methods. For now, I am leaving empty)

@Injectable({
    scope: Scope.DEFAULT
})
export class PinoLoggerService implements LoggerService{
    constructor(private appUtilService: AppUtilService) {

    }

    logService = (fileNameString): pino.Logger => {
        return pino({
            useLevelLabels: true,
            prettyPrint: this.appUtilService.isDevEnv(),
            // tslint:disable-next-line: object-literal-sort-keys
            messageKey: APP_MESSAGE_KEY,
            level: this.appUtilService.getLogLevel(),
            redact: {
                paths: APP_LOG_REDACT,
                censor: '**SECRET-INFO**'
            },
            base: {
                hostName: os.hostname(),
                platform: os.platform(),
                processId: process.pid,
                timestamp: this.appUtilService.getCurrentLocaleTimeZone(),
                // tslint:disable-next-line: object-literal-sort-keys
                fileName: this.appUtilService.getFileName(fileNameString),
            },
        });
    }

    debug(message: any, context?: string): any {
    }

    error(message: any, trace?: string, context?: string): any {
    }

    log(message: any, context?: string): any {
    }

    warn(message: any, context?: string): any {
    }

}

This is a sample redact path: [req.headers["Contet-typr"] Now the redact path is incorrect obviously, but cannot be determined unless a specific req is made and that logger is implemented somewhere.

Consider a scenario where there are interceptors (example like logging interceptor) which is set as global interceptor. The one below is a logging interceptor which tries to redact the "content-type" header but since the redact array path is incorrect, this will result in a run time exception being thrown.

@Injectable()
export class LoggingInterceptor implements NestInterceptor{
    constructor(private readonly logService: PinoLoggerService) {

    }

    intercept(context: ExecutionContext, next: CallHandler<any>): Observable<any> | Promise<Observable<any>> {
        const headers = context.switchToHttp().getRequest().headers;
        console.log('Logging the incoming req', headers);
        return next.handle();
    }

}

And the way to capture these exceptions is via the exception filter and not via Interceptor.catchError

export class CustomExceptionFilter implements ExceptionFilter {
    catch(exception: any, host: ArgumentsHost): any {
        console.log('coming in first filter');
        const errModel = new ErrorModel('AppErrorType');
        errModel.name = exception.name;
        errModel.errorMessage = exception.message;
        errModel.errorCode = '500';
        errModel.errorFields = null;
        errModel.reason = 'Internal server failure';
        errModel.stack = exception.stack;
        exception['errorModel'] = errModel;
        console.log('exce:', exception);
        const reply: FastifyReply<any> = host.switchToHttp().getResponse();
        reply.send({error: errModel}).status(parseInt(errModel.errorCode));
    }

}

Hope this helps.

Upvotes: 2

Related Questions