DEV Community

Cover image for Error Handling in NestJS: Best Practices and Examples
Geampiere Jaramillo
Geampiere Jaramillo

Posted on

2

Error Handling in NestJS: Best Practices and Examples

In any real-world backend application, proper error handling is essential. It ensures that your system doesn't crash unexpectedly, responds clearly to clients, and makes debugging easier for developers. Luckily, NestJS provides powerful tools to do this cleanly and professionally.

Let’s explore how to handle errors effectively in NestJS using built-in exceptions, global filters, and custom strategies.


🚨 Why is Error Handling Important?

Errors happen: invalid input, missing resources, database failures, unreachable services… A backend that doesn't handle these gracefully will:

  • Crash or hang
  • Leak sensitive data
  • Return unclear or inconsistent responses

NestJS allows us to build resilient APIs that fail smart, not hard.


✅ Level 1: HTTP Exceptions (Built-in)

NestJS offers ready-to-use exception classes like BadRequestException, NotFoundException, UnauthorizedException, etc.

Example:

@Get(':id')
async findOne(@Param('id') id: string) {
  const user = await this.userService.findById(id);
  if (!user) {
    throw new NotFoundException(`User with ID ${id} not found`);
  }
  return user;
}
Enter fullscreen mode Exit fullscreen mode

👉 Nest will automatically respond with a 404 and a structured JSON response.


🧱 Level 2: Global Exception Filter

For centralized error handling, you can create a custom exception filter to catch and format all uncaught exceptions globally.

Example:


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

@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
  private readonly logger = new Logger(AllExceptionsFilter.name);

  catch(exception: unknown, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();

    const status = exception instanceof HttpException
      ? exception.getStatus()
      : 500;

    const message = exception instanceof HttpException
      ? exception.getResponse()
      : 'Internal server error';

    this.logger.error('Exception caught', exception instanceof Error ? exception.stack : '');

    response.status(status).json({
      statusCode: status,
      message,
      timestamp: new Date().toISOString(),
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

Enable it globally:


import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { AllExceptionsFilter } from './common/filters/all-exceptions.filter';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalFilters(new AllExceptionsFilter());
  await app.listen(3000);
}
bootstrap();
Enter fullscreen mode Exit fullscreen mode

🔁 Now all unhandled errors are caught and returned with a unified format.


🎯 Level 3: Custom Exceptions

You can define your own domain-specific exceptions for business logic clarity.


import { BadRequestException } from '@nestjs/common';

export class DuplicatedEmailException extends BadRequestException {
  constructor(email: string) {
    super(`The email ${email} is already registered`);
  }
}
Enter fullscreen mode Exit fullscreen mode

Use it like:


if (userExists) {
  throw new DuplicatedEmailException(email);
}
Enter fullscreen mode Exit fullscreen mode

🧪 Level 4: Automatic Validation Errors with class-validator

When using DTOs with class-validator and ValidationPipe, NestJS handles validation errors out of the box.


@IsEmail()
email: string;

@MinLength(6)
password: string;
Enter fullscreen mode Exit fullscreen mode

Enable it globally in main.ts:

app.useGlobalPipes(new ValidationPipe());
Enter fullscreen mode Exit fullscreen mode

🔐 This will respond with 400 Bad Request if the input doesn't meet the defined constraints.


🧵 Bonus: Error Handling in Microservices

If you're using NestJS with Kafka, RabbitMQ, or other transport layers, use RpcException inside your message handlers:


@MessagePattern('create-user')
async createUser(data: CreateUserDto) {
  try {
    return await this.userService.create(data);
  } catch (error) {
    throw new RpcException('Error creating user');
  }
}
Enter fullscreen mode Exit fullscreen mode

This prevents crashes and enables proper retry or dead-letter strategies in your messaging system.


🧠 Final Thoughts

Error handling in NestJS is not just about catching bugs—it's about building services that:

  • Are user-friendly (clear responses)
  • Are developer-friendly (structured logs and debugging)
  • Follow clean architecture and domain logic boundaries

By combining exception filters, DTO validation, and custom errors, you’ll write APIs that are robust, scalable, and production-ready.

ACI image

ACI.dev: Best Open-Source Composio Alternative (AI Agent Tooling)

100% open-source tool-use platform (backend, dev portal, integration library, SDK/MCP) that connects your AI agents to 600+ tools with multi-tenant auth, granular permissions, and access through direct function calling or a unified MCP server.

Star our GitHub!

Top comments (0)

Tiger Data image

🐯 🚀 Timescale is now TigerData: Building the Modern PostgreSQL for the Analytical and Agentic Era

We’ve quietly evolved from a time-series database into the modern PostgreSQL for today’s and tomorrow’s computing, built for performance, scale, and the agentic future.

So we’re changing our name: from Timescale to TigerData. Not to change who we are, but to reflect who we’ve become. TigerData is bold, fast, and built to power the next era of software.

Read more

👋 Kindness is contagious

Explore this insightful piece, celebrated by the caring DEV Community. Programmers from all walks of life are invited to contribute and expand our shared wisdom.

A simple "thank you" can make someone’s day—leave your kudos in the comments below!

On DEV, spreading knowledge paves the way and fortifies our camaraderie. Found this helpful? A brief note of appreciation to the author truly matters.

Let’s Go!