Exception filters are the final layer that turns a thrown error into an HTTP response. This lesson covers Nest's built-in exception layer and how to write a custom filter.
How Nest Handles Thrown Exceptions
By default, NestJS ships a built-in global exception filter that catches any unhandled exception, formats known HttpException instances into their corresponding status code and JSON body, and falls back to a generic 500 Internal Server Error for anything else.
throw new NotFoundException('Cat not found');
// Automatically becomes:
// HTTP 404
// { "statusCode": 404, "message": "Cat not found", "error": "Not Found" }
You never have to manually catch and format HttpException subclasses yourself, Nest's default exception filter already does this for the entire application.
Omitting a type from @Catch() makes a filter catch everything, including non-HttpException errors thrown from anywhere in the application, useful for a top-level safety net that logs unexpected errors and returns a generic 500 without leaking internal details.
@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
const status =
exception instanceof HttpException ? exception.getStatus() : 500;
// log full details server-side, return a generic message to the client
}
}
How Multiple Filters Interact
A more specific filter (catching HttpException) registered at the controller level takes precedence over a catch-all global filter for exceptions of that specific type, letting you layer general and specific error handling together.
Common Mistakes
Writing a custom filter for HttpException but forgetting to also handle unexpected non-HTTP errors.
Leaking internal stack traces or database error details directly to API clients.
Registering a global filter with useGlobalFilters() when it needs injected dependencies (use APP_FILTER instead).
Duplicating Nest's default formatting logic instead of extending or reusing it where possible.
Custom filters implement ExceptionFilter and are declared with @Catch().
A catch-all filter (@Catch() with no arguments) is a useful top-level safety net.
Global filters needing dependency injection should be registered via the APP_FILTER token.
Pro Tip
Always pair a catch-all global exception filter with structured server-side logging (including the original error, not just its message), it's the difference between debugging a production incident in minutes versus guessing blindly from a generic 500 response.
You now understand exception filters. Next, look specifically at HTTP Exceptions, the built-in exception classes you'll throw most often.