Bordered avatar

Street Learner

Author
6 min read

Last Updated: a year ago

Mastering Custom Exception Filters in NestJS: Context-Aware Error Handling

Mastering Custom Exception Filters in NestJS: Context-Aware Error Handling

In modern backend development, handling errors efficiently is crucial for maintainable and user-friendly applications. NestJS provides a robust exception handling mechanism with exception filters, allowing developers to customize error responses, log important details, and manage errors differently depending on the context.

This guide dives deep into Custom Exception Filters, including context-aware filters, MongoDB error handling, and GraphQL integration, giving you a production-ready approach to error management.

What Are Exception Filters in NestJS?

Exception filters in NestJS are classes that intercept thrown exceptions in controllers, services, or middleware. They allow you to:

  • Handle errors globally or locally
  • Customize the response sent to the client
  • Integrate logging and monitoring

NestJS comes with built-in filters, but for advanced production applications, you need custom, context-aware filters.

Simple Example: Handling HTTP Exceptions

import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();
    const status = exception.getStatus();

    response.status(status).json({
      statusCode: status,
      message: exception.message,
      timestamp: new Date().toISOString(),
    });
  }
}

✅ This ensures consistent error formatting across all HTTP exceptions.

Context-Aware Exception Filters

In production, errors need to be handled differently depending on the request type, environment, or error type. Context-aware filters allow you to:

  • Log detailed information for developers
  • Send user-friendly messages to clients
  • Handle different contexts (REST, GraphQL, WebSocket)

Example:

import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus } from '@nestjs/common';

@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
  catch(exception: unknown, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();
    const request = ctx.getRequest();

    const status =
      exception instanceof HttpException
        ? exception.getStatus()
        : HttpStatus.INTERNAL_SERVER_ERROR;

    const message =
      exception instanceof HttpException
        ? exception.getResponse()
        : 'Internal server error';

    // Log error with request context
    console.error(`[${request.method}] ${request.url}`, exception);

    response.status(status).json({
      statusCode: status,
      path: request.url,
      timestamp: new Date().toISOString(),
      message,
    });
  }
}

This filter logs errors with request metadata while providing a clean JSON response to clients.

Applying Exception Filters

Global Filters

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { AllExceptionsFilter } from './common/filters/all-exceptions.filter';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalFilters(new AllExceptionsFilter());
  await app.listen(3000);
}
bootstrap();

Local Filters

import { Controller, Get, UseFilters } from '@nestjs/common';
import { HttpExceptionFilter } from '../common/filters/http-exception.filter';

@Controller('products')
@UseFilters(HttpExceptionFilter)
export class ProductsController {
  @Get()
  findAll() {
    throw new Error('Something went wrong');
  }
}

Local filters are ideal for route-specific error handling.

GraphQL Context-Aware Filters

GraphQL wraps errors differently than REST. NestJS provides the GqlExceptionFilter for GraphQL:

import { GqlExceptionFilter, ArgumentsHost, Catch } from '@nestjs/graphql';
import { HttpException } from '@nestjs/common';

@Catch()
export class GqlHttpExceptionFilter implements GqlExceptionFilter {
  catch(exception: any, host: ArgumentsHost) {
    if (exception instanceof HttpException) {
      return {
        statusCode: exception.getStatus(),
        message: exception.message,
      };
    }
    return { statusCode: 500, message: 'Internal server error' };
  }
}

You can apply this globally to a GraphQL module for consistent error formatting.

Handling MongoDB Errors Gracefully

Database errors should be managed differently than validation or business logic errors. Example MongoDB filter:

import { ExceptionFilter, Catch, ArgumentsHost, HttpStatus } from '@nestjs/common';
import { MongoError } from 'mongodb';

@Catch(MongoError)
export class MongoExceptionFilter implements ExceptionFilter {
  catch(exception: MongoError, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();

    response.status(HttpStatus.BAD_REQUEST).json({
      statusCode: HttpStatus.BAD_REQUEST,
      message: exception.message,
      timestamp: new Date().toISOString(),
    });
  }
}

✅ This ensures database errors are formatted consistently for client consumption.

Why Context-Aware Filters Are Essential

  1. Standardized Responses – All endpoints return consistent error structure
  2. Detailed Logging – Errors are logged with request context for debugging
  3. Flexible Behavior – REST, GraphQL, WebSocket, and DB errors can be handled differently
  4. User-Friendly Messages – Internal errors can be hidden from clients
  5. Production-Ready – Combine global and local filters for maintainable error handling

Senior Developer Tips

  • Wrap unexpected exceptions in a global filter
  • Use local filters for route-specific scenarios
  • Separate internal logs and user-facing messages
  • Combine filters with interceptors for metrics or caching
  • Integrate with monitoring tools like Sentry for production

Real-World Use Cases

  • REST API with consistent error messages
  • GraphQL API with custom errors
  • MongoDB validation and duplicate key errors
  • Logging detailed error information for monitoring
  • Multi-service applications needing centralized error handling

Related Stories



Performance Optimization of NestJS Applications: A Complete Guide

Performance Optimization of NestJS Applications: A Complete Guide

Optimizing the performance of a NestJS application is critical for building scalable, fast, and production-ready APIs. Even though NestJS is a high-performance framework, improper coding practices, unoptimized database queries, and lack of caching can slow down your application.

Bordered avatar

Street Learner

21 Jan 2025

Mastering High-Performance Interceptors in NestJS for Scalable APIs

Mastering High-Performance Interceptors in NestJS for Scalable APIs

NestJS interceptors are one of the most powerful tools in the framework, enabling developers to transform responses, cache results, log performance, and optimize requests. For large-scale applications, building high-performance interceptors is essential to improve speed, maintainability, and scalability.

Bordered avatar

Street Learner

18 Jan 2025