DEV Community

Vivek Chauhan
Vivek Chauhan

Posted on

Error Handling and Logging in NestJS: Best Practices

Nestjs
When building applications with NestJS, handling errors and implementing robust logging are crucial aspects of ensuring your application’s reliability and maintainability. In this comprehensive guide, we’ll explore the best practices for error handling and logging in NestJS, specifically tailored for beginners. So, fasten your seatbelts and let’s embark on this journey to master error handling and logging in NestJS!

Understanding Error Handling in NestJS

Error handling is the process of gracefully handling and managing unexpected situations that may arise during the execution of your application. NestJS offers a structured approach to handle errors effectively. The primary mechanism to achieve this is by using Exception Filters. Exception Filters allow you to intercept thrown exceptions and take appropriate actions, such as returning meaningful error responses to clients.

Let’s take a look at how to implement an Exception Filter:

import { ExceptionFilter, Catch, ArgumentsHost, HttpException } 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();

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

In this example, we’ve created an HttpExceptionFilter, which catches any HttpException that might occur in our application. It then extracts relevant information from the exception and sends a structured JSON response to the client, including the status code, timestamp, request path, and error message.

To apply this filter globally to your application, you can use the app.useGlobalFilters() method in your main.ts file:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { HttpExceptionFilter } from './http-exception.filter';

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

With this setup, all HttpExceptions will be caught by our HttpExceptionFilter, ensuring consistent error responses throughout the application.

Handling Custom Exceptions

While NestJS provides several built-in exceptions, you’ll often encounter scenarios where you need to define your custom exceptions. Creating custom exceptions is simple and allows you to provide specific details about the error to the client.

Let’s say we want to create a custom NotFoundException:

export class NotFoundException extends HttpException {
  constructor(message: string) {
    super(message, HttpStatus.NOT_FOUND);
  }
}
Enter fullscreen mode Exit fullscreen mode

We can then use this custom exception within our application’s services or controllers:

import { Injectable, NotFoundException } from '@nestjs/common';
import { Task } from './interfaces/task.interface';

@Injectable()
export class TaskService {
  private tasks: Task[] = [];

  getTaskById(id: string): Task {
    const task = this.tasks.find((task) => task.id === id);
    if (!task) {
      throw new NotFoundException(`Task with ID ${id} not found.`);
    }
    return task;
  }
}
Enter fullscreen mode Exit fullscreen mode

In this example, if the requested task with a specific id is not found, we throw the NotFoundException, which will be caught by our global HttpExceptionFilter. This allows us to send a user-friendly response back to the client without leaking sensitive implementation details.

Logging in NestJS

Effective logging is essential for monitoring the behavior of your application and diagnosing potential issues. NestJS provides a powerful logging mechanism out of the box, allowing you to choose from various logging levels, log formatters, and transports.

By default, NestJS uses the Logger class to handle application logs. To use the default logger in your services or controllers, you can inject it using the @Logger() decorator:

import { Injectable, Logger } from '@nestjs/common';

@Injectable()
export class TaskService {
  private logger = new Logger(TaskService.name);

  // ...

  someMethod() {
    this.logger.log('This is a log message.');
    this.logger.debug('Debugging some process...');
    this.logger.warn('Warning: Something seems off!');
    this.logger.error('Oops! An error occurred.');
  }
}
Enter fullscreen mode Exit fullscreen mode

The Logger class provides methods for various log levels, such as log, debug, warn, and error. Each log level corresponds to a specific severity, and you can configure the logging behavior in your NestJS application.

Customizing Logging Behavior

If you need more control over your application’s logging behavior, NestJS allows you to create custom loggers by implementing the LoggerService interface.

Let’s create a fun custom logger that writes logs to a file and also includes emojis!

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

export class EmojiLogger implements LoggerService {
  log(message: string) {
    this.writeToFile('📢 ' + message);
  }

  error(message: string, trace: string) {
    this.writeToFile('❌ ' + message);
    this.writeToFile('🔍 Stack Trace: ' + trace);
  }

  warn(message: string) {
    this.writeToFile('⚠️ ' + message);
  }

  debug(message: string) {
    this.writeToFile('🐞 ' + message);
  }

  private writeToFile(message: string) {
    // Implement the logic to write logs to a file here.
    console.log(message); // For demonstration purposes, we'll just log to the console.
  }
}
Enter fullscreen mode Exit fullscreen mode

Now, to use our EmojiLogger, let’s update the main.ts file:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { EmojiLogger } from './emoji-logger';

async function bootstrap() {
  const app = await NestFactory.create(AppModule, { logger: new EmojiLogger() });
  await app.listen(3000);
}
bootstrap();
Enter fullscreen mode Exit fullscreen mode

With this setup, our application will use the fun EmojiLogger to log messages. Feel free to customize the writeToFile method to write logs to a file or any other preferred destination.

Conclusion

In this article, we’ve explored the best practices for error handling and logging in NestJS. We’ve learned how to use Exception Filters to gracefully handle errors and send structured error responses to clients. Additionally, we’ve seen how to create custom exceptions for more specific error reporting. Furthermore, we’ve delved into the world of logging and discovered how to use the default Logger class and create custom loggers, like our EmojiLogger.

By mastering error handling and logging in NestJS, you’re well on your way to building robust and reliable applications. So, keep experimenting, stay curious, and be happy coding! 🚀🌟

Top comments (0)