Exception Filters in NestJS: Handling Errors Gracefully

Exception Filters in NestJS: Handling Errors Gracefully

Introduction

Exception filters are an elegant way to handle errors and exceptions in NestJS. This in-depth guide explains what exception filters are, why they matter, and how to use them effectively with code examples.

What are Exception Filters in NestJS?

NestJS exception filters are classes that let you control the flow when exceptions are thrown anywhere in your application. They allow you to:

  • Catch and handle exceptions across the app
  • Gracefully return error responses to clients
  • Log and monitor errors that occur
  • Extend base exception classes with custom logic

So in summary, think of exception filters as global error handlers for your NestJS app.

Why are Exception Filters Useful?

Here are some of the benefits that exception filters provide:

  • Global error handling – Filters let you consolidate error handling code in one place instead of duplicating try/catch blocks everywhere.
  • Cleaner controllers – Logic for returning errors can be extracted out of controllers into filters, leaving controllers focused on business logic.
  • Custom responses – You can customize the HTTP responses sent back to clients when errors occur.
  • Logging – Exceptions can be logged in one place inside a filter for easier debugging.
  • Shared logic – Common error handling logic can be extracted into base filter classes that are shared across the app.

So exception filters help you manage errors in a cleaner and more scalable way.

Built-in Exception Filters

NestJS includes some built-in exception filters out of the box:

  • HttpExceptionFilter – Catches exceptions that extend the HttpException class and returns proper error responses.
  • AllExceptionsFilter – Catches all unhandled exceptions and returns a generic 500 error response.
  • RpcExceptionFilter – Catches exceptions for microservices using RPC and handles them.

So for common cases, you may not need to write your own filters initially. But learning to build custom filters is still useful.

Creating a Custom Exception Filter

It’s easy to create your own reusable exception filters in NestJS. Here are the steps:

1. Import Catch and ArgumentsHost:

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

2. Decorate class with @Catch() and implement ExceptionFilter:

@Catch() 
export class HttpExceptionFilter implements ExceptionFilter {
  // ...
}

3. Implement catch(exception, host) method:

catch(exception: any, host: ArgumentsHost) {
  // handle exception
}
  1. Get response and request objects from host
  2. Log exception, set response status code, return JSON response, etc.

And that’s the basic process – your filter class will now catch all exceptions app-wide.

Exception Filter Example

Let’s look at an example exception filter class:

@Catch()
export class AppExceptionFilter implements ExceptionFilter {

  catch(exception: any, host: ArgumentsHost) {

    const ctx = host.switchToHttp();

    const response = ctx.getResponse();
    const request = ctx.getRequest();

    const status = exception.getStatus
      ? exception.getStatus() 
      : HttpStatus.INTERNAL_SERVER_ERROR;

    const errorResponse = {
      statusCode: status,
      timestamp: new Date().toISOString(),
      path: request.url,
    }

    if (status === HttpStatus.INTERNAL_SERVER_ERROR) {
      // send to logger
      logger.error(...);
    }

    response.status(status).json(errorResponse);
  }

}

Here we:

  • Get access to response and request objects
  • Check exception type and set response status code
  • Construct JSON response body
  • Optionally log errors
  • Return JSON error response

This provides a clean way to handle exceptions.

Registering Exception Filters

To use an exception filter, you need to register it so Nest can activate it:

@Module({
  providers: [
    {
      provide: APP_FILTER, 
      useClass: AppExceptionFilter 
    }
  ]
})
export class AppModule {}

This registers AppExceptionFilter as the default global filter.

You can also register filters at the controller level to scope them:

@Controller('users', {
  exceptionsFilter: UserExceptionFilter
})
export class UsersController {}

So registering filters globally or locally lets you control where they are active.

Multiple Filters

You can chain multiple exception filters to create tiered error handling:

const app = await NestFactory.create(AppModule);

app.useGlobalFilters(new HttpExceptionFilter());
app.useGlobalFilters(new AllExceptionsFilter());

The filters will execute sequentially in the order registered.

Inheriting Filter Logic

You can create base filter classes with shared logic and extend them for code reuse:

@Catch()
export class BaseFilter implements ExceptionFilter {
  // shared logic
}

@Catch()
export class UserFilter extends BaseFilter {
  // custom logic 
}

Exception Filters vs Guards

Both exception filters and guards deal with errors, but there is a difference:

  • Guards – Handle errors that occur before reaching route handlers. Focus on authentication, authorization, validation errors.
  • Filters – Handle errors that occur during route handlers. Focus on application-level errors.

So in summary:

  • Guards: Before, AuthN/Z, Validation
  • Filters: During, App Errors

Conclusion

Here are the key points we covered about exception filters in NestJS:

  • Exception filters are global error handlers that consolidate error handling logic.
  • They allow you to cleanly send custom response objects when errors occur.
  • Filters should be used to catch application-level errors happening in services, repositories etc.
  • Filters can be registered locally or globally to control their scope.
  • Inheriting from base filter classes allows logic reuse.
  • Filters pair nicely with guards for authentication and request validation.

Using exception filters helps make your NestJS apps more resilient, maintainable and cleaner.

Frequently Asked Questions

Q: What types of exceptions should be handled by exception filters?

Exception filters are best suited for catching and handling general application-level exceptions vs low-level issues like parsing/validation.

Q: Can I use the same exception filter for different controllers?

Yes, exception filters are meant to be reused across your application. Register them globally to share across multiple controllers.

Q: Should I log exceptions inside or outside exception filters?

Logging related to request handling can be done inside filters. More detailed debug logging may be better handled outside filters in services/repos.

Q: How do I decide between a guard or exception filter?

Guards focus on authentication, authorization and parameters/inputs. Filters focus on application exceptions during business logic processing.

Q: Can I use multiple exception filters at once?

Yes, NestJS allows chaining multiple exception filters together. They will process sequentially in the order registered.

Related Resources

Leave a Reply

Your email address will not be published. Required fields are marked *