NestJS Exception Filters
An exception filter catches errors thrown anywhere in your application and transforms them into structured HTTP responses. When a service throws an unexpected error or a guard blocks a request, NestJS routes that exception to the appropriate filter. The filter decides what status code and response body the client receives.
The Default Exception Filter
NestJS includes a built-in global exception filter that handles all unhandled exceptions. When your code throws a NestJS HTTP exception, the default filter produces a clean JSON error response automatically:
throw new NotFoundException('User not found');
→ Response:
{
"statusCode": 404,
"message": "User not found",
"error": "Not Found"
}
When your code throws a generic JavaScript Error that is not an HTTP exception, the default filter returns a 500 Internal Server Error to avoid leaking implementation details to the client.
Creating a Custom Exception Filter
A filter is a class decorated with @Catch() that implements ExceptionFilter:
import {
ExceptionFilter, Catch, ArgumentsHost,
HttpException, HttpStatus
} from '@nestjs/common';
import { Request, Response } from 'express';
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception.getStatus();
const exceptionResponse = exception.getResponse();
response.status(status).json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
message:
typeof exceptionResponse === 'string'
? exceptionResponse
: (exceptionResponse as any).message,
});
}
}
Custom Exception Filter Response
Before (default filter):
{
"statusCode": 404,
"message": "User not found",
"error": "Not Found"
}
After (custom filter):
{
"statusCode": 404,
"timestamp": "2025-01-15T10:30:00.000Z",
"path": "/users/999",
"message": "User not found"
}
Applying Exception Filters
// On a single route
@Get(':id')
@UseFilters(HttpExceptionFilter)
findOne(@Param('id', ParseIntPipe) id: number) { ... }
// On an entire controller
@Controller('users')
@UseFilters(HttpExceptionFilter)
export class UsersController { ... }
// Globally — catches every exception in the application
app.useGlobalFilters(new HttpExceptionFilter());
Catching All Exceptions
Pass no argument to @Catch() to catch every exception, regardless of type:
@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status =
exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
const message =
exception instanceof HttpException
? exception.message
: 'Internal server error';
response.status(status).json({
statusCode: status,
message,
path: request.url,
timestamp: new Date().toISOString(),
});
}
}
Creating Custom Exception Classes
Define custom exceptions for specific business errors. This keeps error semantics close to the domain rather than relying on generic HTTP status codes:
import { HttpException, HttpStatus } from '@nestjs/common';
export class InsufficientStockException extends HttpException {
constructor(productId: number, available: number) {
super(
{
statusCode: HttpStatus.CONFLICT,
error: 'Insufficient Stock',
message: `Product ${productId} has only ${available} units available`,
},
HttpStatus.CONFLICT,
);
}
}
// Usage in service
if (product.stock < requestedQuantity) {
throw new InsufficientStockException(product.id, product.stock);
}
Exception Filter Pipeline Position
HTTP Request
|
v
Middleware → Guards → Interceptors → Pipes → Controller
|
Exception thrown
|
v
Exception Filter
|
v
Error Response sent
Logging Exceptions
Exception filters are the correct place to log errors centrally. Every unhandled error passes through the filter, giving you one place to record stack traces, alert monitoring services, or write to a log file:
@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
private readonly logger = new Logger(AllExceptionsFilter.name);
catch(exception: unknown, host: ArgumentsHost) {
this.logger.error(
exception instanceof Error ? exception.message : 'Unknown error',
exception instanceof Error ? exception.stack : '',
);
// ... send response
}
}
Exception filters give you complete control over error presentation. Rather than letting raw errors bubble up to clients with framework-generated messages, you decide exactly what error format every consumer of your API receives — consistent, informative, and safe from implementation leaks.
