NestJS — Enterprise Node.js

Exception Filters & Error Handling

16 min Lesson 15 of 30

Exception Filters & Error Handling

Things go wrong: a record is missing, input is bad, a third party times out. NestJS has a built-in exceptions layer that turns thrown errors into proper HTTP responses — and exception filters let you customise exactly how those responses look.

Built-in HTTP exceptions

Instead of crafting error responses by hand, throw one of NestJS's built-in exceptions. The framework catches it and sends the right status code and JSON body:

import { NotFoundException, BadRequestException } from '@nestjs/common'; @Injectable() export class UsersService { findOne(id: number) { const user = this.users.find((u) => u.id === id); if (!user) { throw new NotFoundException(`User ${id} not found`); } return user; } }

Common ones: BadRequestException (400), UnauthorizedException (401), ForbiddenException (403), NotFoundException (404), ConflictException (409), and InternalServerErrorException (500).

The base HttpException

For a custom status or body, throw HttpException directly:

import { HttpException, HttpStatus } from '@nestjs/common'; throw new HttpException('Custom message', HttpStatus.I_AM_A_TEAPOT);
Throw, do not return errors. You simply throw from anywhere — a service, a pipe, a guard. The exceptions layer catches it at the boundary and formats the response. Your handler stays clean.

Writing an exception filter

An exception filter takes full control over the response for the exceptions it catches. Decorate it with @Catch() and implement catch(exception, host):

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, timestamp: new Date().toISOString(), message: exception.message, }); } }

@Catch(HttpException) scopes the filter to a type; @Catch() with no argument catches everything, useful for a global safety net.

Applying filters

import { UseFilters } from '@nestjs/common'; @UseFilters(HttpExceptionFilter) // method or controller level @Get() findAll() {} // or globally: app.useGlobalFilters(new HttpExceptionFilter());
A global filter is the place for a consistent error shape. Define one filter that formats every error the same way (status, timestamp, message, request id) and register it globally — clients then get predictable error responses across the whole API.
Do not leak internal details. In production, an exception filter should hide stack traces and raw error messages for 500-level errors. Log the real error server-side, but send the client a generic message.

Summary

Throw built-in exceptions like NotFoundException (or HttpException for custom cases) and let the exceptions layer format the HTTP response. For full control, write an exception filter with @Catch() and catch(exception, host), then bind it per-route or globally. A global filter gives your API one consistent, safe error format. This completes the request lifecycle — last in this phase: custom decorators.