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:
- Resolves the filter instance from the IoC container
- Extracts the
catchfunction reference - Matches the thrown exception against registered types stored in
FILTER_CATCH_EXCEPTIONSmetadata - 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 inpackages/common/interfaces/exceptions/exception-filter.interface.ts - Metadata reflection:
@Catch()stores exception types using theFILTER_CATCH_EXCEPTIONSkey, read byBaseExceptionFilterContext.reflectCatchExceptionsinpackages/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:
ArgumentsHostprovidesswitchToHttp(),switchToWs(), andswitchToRpc()for transport-agnostic error handling - Filter resolution:
BaseExceptionFilterContextorchestrates filter instantiation and execution, whileRouterExceptionFiltershandles 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →