Nest comes with a built-in exceptions layer which is responsible for processing all unhandled exceptions across an application. When an exception is not handled by your application code, it is caught by this layer, which then automatically sends an appropriate user-friendly response.
Out of the box, this action is performed by a built-in global exception filter, which handles exceptions of type
HttpException (and subclasses of it). When an exception is unrecognized (is neither
HttpException nor a class that inherits from
HttpException), the built-in exception filter generates the following default JSON response:
Nest provides a built-in
HttpException class, exposed from the
@nestjs/common package. For typical HTTP REST/GraphQL API based applications, it's best practice to send standard HTTP response objects when certain error conditions occur.
For example, in the
CatsController, we have a
findAll() method (a
GET route handler). Let's assume that this route handler throws an exception for some reason. To demonstrate this, we'll hard-code it as follows:
info Hint We used the
HttpStatushere. This is a helper enum imported from the
When the client calls this endpoint, the response looks like this:
HttpException constructor takes two required arguments which determine the
responseargument defines the JSON response body. It can be a
objectas described below.
statusargument defines the HTTP status code.
By default, the JSON response body contains two properties:
statusCode: defaults to the HTTP status code provided in the
message: a short description of the HTTP error based on the
To override just the message portion of the JSON response body, supply a string
response argument. To override the entire JSON response body, pass an object in the
response argument. Nest will serialize the object and return it as the JSON response body.
The second constructor argument -
status - should be a valid HTTP status code.
Best practice is to use the
HttpStatus enum imported from
Here's an example overriding the entire response body:
Using the above, this is how the response would look:
In many cases, you will not need to write custom exceptions, and can use the built-in Nest HTTP exception, as described in the next section. If you do need to create customized exceptions, it's good practice to create your own exceptions hierarchy, where your custom exceptions inherit from the base
HttpException class. With this approach, Nest will recognize your exceptions, and automatically take care of the error responses. Let's implement such a custom exception:
ForbiddenException extends the base
HttpException, it will work seamlessly with the built-in exception handler, and therefore we can use it inside the
Nest provides a set of standard exceptions that inherit from the base
HttpException. These are exposed from the
@nestjs/common package, and represent many of the most common HTTP exceptions:
While the base (built-in) exception filter can automatically handle many cases for you, you may want full control over the exceptions layer. For example, you may want to add logging or use a different JSON schema based on some dynamic factors. Exception filters are designed for exactly this purpose. They let you control the exact flow of control and the content of the response sent back to the client.
Let's create an exception filter that is responsible for catching exceptions which are an instance of the
HttpException class, and implementing custom response logic for them. To do this, we'll need to access the underlying platform
Response objects. We'll access the
Request object so we can pull out the original
url and include that in the logging information. We'll use the
Response object to take direct control of the response that is sent, using the
info Hint All exception filters should implement the generic
ExceptionFilter<T>interface. This requires you to provide the
catch(exception: T, host: ArgumentsHost)method with its indicated signature.
Tindicates the type of the exception.
@Catch(HttpException) decorator binds the required metadata to the exception filter, telling Nest that this particular filter is looking for exceptions of type
HttpException and nothing else. The
@Catch() decorator may take a single parameter, or a comma-separated list. This lets you set up the filter for several types of exceptions at once.
Let's look at the parameters of the
catch() method. The
exception parameter is the exception object currently being processed. The
host parameter is an
ArgumentsHost is a powerful utility object that we'll examine further in the execution context chapter*. In this code sample, we use it to obtain a reference to the
Response objects that are being passed to the original request handler (in the controller where the exception originates). In this code sample, we've used some helper methods on
ArgumentsHost to get the desired
Response objects. Learn more about
*The reason for this level of abstraction is that
ArgumentsHost functions in all contexts (e.g., the HTTP server context we're working with now, but also Microservices and WebSockets). In the execution context chapter we'll see how we can access the appropriate underlying arguments for any execution context with the power of
ArgumentsHost and its helper functions. This will allow us to write generic exception filters that operate across all contexts.
Let's tie our new
HttpExceptionFilter to the
info Hint The
@UseFilters()decorator is imported from the
We have used the
@UseFilters() decorator here. Similar to the
@Catch() decorator, it can take a single filter instance, or a comma-separated list of filter instances. Here, we created the instance of
HttpExceptionFilter in place. Alternatively, you may pass the class (instead of an instance), leaving responsibility for instantiation to the framework, and enabling dependency injection.
info Hint Prefer applying filters by using classes instead of instances when possible. It reduces memory usage since Nest can easily reuse instances of the same class across your entire module.
In the example above, the
HttpExceptionFilter is applied only to the single
create() route handler, making it method-scoped. Exception filters can be scoped at different levels: method-scoped, controller-scoped, or global-scoped. For example, to set up a filter as controller-scoped, you would do the following:
This construction sets up the
HttpExceptionFilter for every route handler defined inside the
To create a global-scoped filter, you would do the following:
warning Warning The
useGlobalFilters()method does not set up filters for gateways or hybrid applications.
Global-scoped filters are used across the whole application, for every controller and every route handler. In terms of dependency injection, global filters registered from outside of any module (with
useGlobalFilters() as in the example above) cannot inject dependencies since this is done outside the context of any module. In order to solve this issue, you can register a global-scoped filter directly from any module using the following construction:
info Hint When using this approach to perform dependency injection for the filter, note that regardless of the module where this construction is employed, the filter is, in fact, global. Where should this be done? Choose the module where the filter (
HttpExceptionFilterin the example above) is defined. Also,
useClassis not the only way of dealing with custom provider registration. Learn more here.
You can add as many filters with this technique as needed; simply add each to the providers array.
In order to catch every unhandled exception (regardless of the exception type), leave the
@Catch() decorator's parameter list empty, e.g.,
In the example above the filter will catch each exception thrown, regardless of its type (class).
Typically, you'll create fully customized exception filters crafted to fulfill your application requirements. However, there might be use-cases when you would like to simply extend the built-in default global exception filter, and override the behavior based on certain factors.
In order to delegate exception processing to the base filter, you need to extend
BaseExceptionFilter and call the inherited
warning Warning Method-scoped and Controller-scoped filters that extend the
BaseExceptionFiltershould not be instantiated with
new. Instead, let the framework instantiate them automatically.
The above implementation is just a shell demonstrating the approach. Your implementation of the extended exception filter would include your tailored business logic (e.g., handling various conditions).
Global filters can extend the base filter. This can be done in either of two ways.
The first method is to inject the
HttpServer reference when instantiating the custom global filter:
The second method is to use the
APP_FILTER token as shown here.