[NestJs] Intercept axios responses and throw built-in HTTP errors for the exception filter
Tags: node, typescript, nestjs, beginners
Published: 2022-10-25 10:11:08

###### Context
How do we intercept the external http calls made by our NestJs server, so we can log and better handle the errors?
`NestJs Interceptors` are able to process the requests and the responses made via the controllers, not the external http calls that we might need as part of a controller request. 

###### Solutions
One solution would be to add a `catchError` on the http call. [NestJs documentation](https://docs.nestjs.com/techniques/http-module#full-example) provides a full example and extensive information on `HttpModule` and `HttpService` from `@nestjs/axios package`.

What if we already have a couple of axios requests and we don't want to add catchError block on each request? Here comes the second solution: **axios response interceptors** and **exception filters**.

First, let's consider this call as part of our service, where (hypothetically) our http call will throw an error:

```javascript
//app.service.ts
getCats(): Observable<any> {
    return this.httpService.get('http://localhost:3001/cats').pipe(
      map((data) => {
        console.log('Do something');
        return data;
      }),
    );
  }
```

Let's configure the `axios response interceptor`. We're going to add this snippet as part of the bootstrap function. All `HttpService` methods return an `AxiosResponse` wrapped in an `Observable` object, responses that we're going to intercept via this code:

```javascript
// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { HttpService } from '@nestjs/axios';
import { InternalServerErrorException } from '@nestjs/common';

async function bootstrap() {
  const httpService = new HttpService();
  const app = await NestFactory.create(AppModule);

  httpService.axiosRef.interceptors.response.use(
    (response) => {
      return response;
    },
    (error) => {
      console.error('Internal server error exception', error);
      throw new InternalServerErrorException();
    },
  );

  await app.listen(3000);
}
```

Responses with status 2** will be handled by the first function, where we simply return the response. In case of error, we log the error and throw a built-in HTTP exception,  `InternalServerErrorException`. Of course, we can implement more advanced error handling here, throwing exceptions depending on the error that we get from the axios code and so on.

Next, what happens with our exception? It's going to be caught by the global http exception filter.

```javascript
//http-exception.filter.ts
import { ArgumentsHost, Catch, ExceptionFilter, 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
    });
  }
}
```

Now that we have the code for the exception filter, let's make the adjustments so we can use it:

```javascript
//app.module.ts
providers: [
    {
      provide: APP_FILTER,
      useClass: HttpExceptionFilter
    }
  ]
```

```javascript
//main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { HttpService } from '@nestjs/axios';
import { HttpExceptionFilter } from './http-exception.filter';
import { InternalServerErrorException } from '@nestjs/common';

async function bootstrap() {
  const httpService = new HttpService();
  const app = await NestFactory.create(AppModule);

  //http-exception as global filter
  app.useGlobalFilters(new HttpExceptionFilter());

  httpService.axiosRef.interceptors.response.use(
    (response) => {
      return response;
    },
    (error) => {
      console.error('Internal server error exception', error);
      throw new InternalServerErrorException();
    },
  );

  await app.listen(3000);
}
```

What we learned as part of this blogpost:
* how to make an external http call via the `HttpService` from `@nestJs/axios` package
* how to intercept the responses of the http calls and throw built-in http exceptions in case of error
* how to intercept the http exceptions via the global http exception filter

In-depth documentation on error handling, axios interceptors and exception filters can be found here:
* [NestJS Exception filters](https://docs.nestjs.com/exception-filters)
* [NestJS Interceptors](https://docs.nestjs.com/interceptors)
* [NestJs HttpModule](https://docs.nestjs.com/techniques/http-module)
* [Axios Interceptors](https://github.com/axios/axios#interceptors)

✨[Github repo](https://github.com/blminami/nestjs-axios-interceptor) with the NestJs setup, exception filter and axios interceptor!

Thank you for reading! 🐾