How to Implement Custom Exception Filters in NestJS: A Complete Guide

To implement custom exception filters in NestJS, create a class implementing the ExceptionFilter interface, decorate it with @Catch() to specify target exception types, and implement the catch(exception, host) method to process errors and format responses.

Exception filters in NestJS provide a centralized mechanism for handling thrown errors across HTTP, WebSocket, and RPC contexts. According to the nestjs/nest source code, the framework treats these filters as first-class citizens with deep integration into the IoC container and metadata reflection systems. Mastering how to implement custom exception filters allows you to standardize error responses, log failures consistently, and keep business logic clean.

Understanding the ExceptionFilter Interface

Every custom filter must honor the contract defined in packages/common/interfaces/exceptions/exception-filter.interface.ts. This interface mandates a single method:

catch(exception: T, host: ArgumentsHost): any

The T generic represents the exception type your filter handles, while ArgumentsHost provides a unified abstraction to access the underlying execution context. This design enables the same filter logic to work across HTTP, WebSockets, and microservice transports without modification.

How NestJS Processes Exception Filters

Metadata Reflection via @Catch

The @Catch(...exceptions) decorator attaches metadata to your filter class using the internal FILTER_CATCH_EXCEPTIONS key. When the application initializes, BaseExceptionFilterContext.reflectCatchExceptions reads this metadata to determine which exception types trigger your filter. This reflection logic resides in packages/core/exceptions/base-exception-filter-context.ts.

The Filter Resolution Flow

When an error bubbles up to the router, Nest resolves applicable filters through the ExceptionFiltersContext—a subclass of BaseExceptionFilterContext that handles concrete filter instantiation. The context performs four critical operations:

  1. Resolves the filter instance from the IoC container
  2. Extracts the catch function reference
  3. Matches the thrown exception against registered types stored in FILTER_CATCH_EXCEPTIONS metadata
  4. Invokes filter.catch(exception, host) for each matching filter

The host parameter exposes platform-specific methods: switchToHttp(), switchToWs(), and switchToRpc(), enabling transport-agnostic error handling.

Creating a Custom Exception Filter

Follow this implementation pattern derived from sample/36-hmr-esm/src/common/filters/http-exception.filter.ts to handle HTTP exceptions:

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

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

    const status = exception.getStatus();

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

The @Catch(HttpException) decorator restricts this filter to HttpException and its subclasses. The ArgumentsHost abstraction allows access to the underlying Express or Fastify response objects through switchToHttp().

Registering Exception Filters

Global Registration

Apply filters across your entire application using useGlobalFilters(), implemented in the INestApplication interface:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { HttpExceptionFilter } from './common/filters/http-exception.filter';

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

Controller-Level Registration

Scope filters to specific controllers or routes using the @UseFilters() decorator:

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

@Controller('cats')
@UseFilters(HttpExceptionFilter)
export class CatsController {
  @Get()
  findAll() {
    // Exceptions here pass through HttpExceptionFilter
  }
}

Catch-All Exception Filters

Create a universal error handler by passing no arguments to @Catch():

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

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

Summary

  • ExceptionFilter interface: Mandates a catch(exception, host) method defined in packages/common/interfaces/exceptions/exception-filter.interface.ts
  • Metadata reflection: @Catch() stores exception types using the FILTER_CATCH_EXCEPTIONS key, read by BaseExceptionFilterContext.reflectCatchExceptions in packages/core/exceptions/base-exception-filter-context.ts
  • Registration options: Use app.useGlobalFilters() for application-wide handling or @UseFilters() for controller-scoped filtering
  • Cross-platform support: ArgumentsHost provides switchToHttp(), switchToWs(), and switchToRpc() for transport-agnostic error handling
  • Filter resolution: BaseExceptionFilterContext orchestrates filter instantiation and execution, while RouterExceptionFilters handles the router-level collection

Frequently Asked Questions

What is the ExceptionFilter interface in NestJS?

The ExceptionFilter interface defines the contract that all exception filters must implement. Located in packages/common/interfaces/exceptions/exception-filter.interface.ts, it requires a single catch(exception: T, host: ArgumentsHost) method where you implement error handling logic and response formatting.

How do I make an exception filter catch all exceptions?

Apply the @Catch() decorator without arguments to your filter class. This registers the filter for every thrown exception, not just specific types. NestJS stores this configuration using the FILTER_CATCH_EXCEPTIONS metadata key and processes it through BaseExceptionFilterContext.reflectCatchExceptions.

Can I use exception filters with WebSockets or microservices?

Yes. The ArgumentsHost parameter provides transport-agnostic access through switchToWs() for WebSockets and switchToRpc() for microservices. The same filter infrastructure in packages/core/exceptions/base-exception-filter-context.ts supports all platforms, with specialized handlers like RpcExceptionsHandler extending the base functionality.

What is the difference between @UseFilters and useGlobalFilters?

@UseFilters() applies filters to specific controllers or methods via decorator metadata, processed during route registration. useGlobalFilters() registers filters on the INestApplication instance, applying them to every route in the application. Global filters are resolved once during bootstrap, while controller-level filters follow the standard dependency injection lifecycle.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →