DEV Community

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

Posted on

1

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.

Top comments (0)

👋 Kindness is contagious

If this post resonated with you, feel free to hit ❤️ or leave a quick comment to share your thoughts!

Okay