Skip to content

exception filters

John Biundo edited this page Aug 25, 2019 · 2 revisions

Exception Filters Chapter

With this chapter, if you want to follow along, check out the exception-filters-start branch and go to the following along section.

If you just want a final version of the project as of the end of the chapter, check out the exception-filters-end branch, and proceed to the upon completion section.

Following along

Refer to the comments below, corresponding to chapter sections, to guide you through the code changes for nest cats as you proceed through the docs chapter.

Exception filters (part 1 section - default exception handling)

As described in the docs, here, if you throw an exception from your code, it's caught by the built-in exception layer provided by Nest. To see this in action, add the following route to src/cats/cats.controller.ts:

  @Get('kaboom')
  badRoute() {
    throw new Error('kaboom');
  }
HTTPie request to test default exception handler

http GET :3000/cats/kaboom

Note that Nest captures this error and prints the stack trace.

[Nest] 2972 - 08/27/2019, 12:19 PM [ExceptionsHandler] kaboom +19224ms Error: kaboom     at CatsController.badRoute (E:\code\nest-cats\dist\cats\cats.controller.js:23:15)     at E:\code\nest-cats\node_modules@nestjs\core\router\router-execution-context.js:36:29 ...

Node has not crashed; you can see this by issuing this request multiple times.

Base exceptions

As described in the Base exceptions section of the docs, you can throw Nest's built-in HttpException. To make testing easier, make a slight change to the code shown in the documentation. Rather than modifying the findAll() method, add the following new route to the CatsController:

@Get('httpexcep')
async getExcep() {
  throw new HttpException({
    status: HttpStatus.FORBIDDEN,
    error: 'This is a custom message',
  }, 403);
}

As mentioned in the docs, you'll also need to import HttpStatus from @nestjs/common. You should also import HttpException from @nestjs/common.

HTTPie request to test custom exception

http GET :3000/cats/httpexcep

Continue reading until you get to the next Exception filters section.

Exception filters section (creating an exception filter)

As explained in the Exception filters section of the docs, go ahead and create the HttpExceptionFilter class in a file called src/common/filters/http-exception.filter.ts, copying the code from that section of the docs. You'll notice from the file path we specified just now that, just as we did with the logging middleware in the last chapter, we'll create this class inside a dedicated filters folder in the src/common folder. The folder structure should now look like:

nest-cats
└───src
    └───cats
    │   └───dto
    │   └───interfaces
    └───common
        └───filters
        └───middleware

Continue reading until you get to the Binding filters section.

Binding filters

Similar to what's explained in the docs, here, let's bind the exception. Varying slightly from the docs to ease testing, bind the exception filter to the GET /cats/httpexcep route we created a few minutes ago.

You can use either the instance (e.g., @UseFilters(new HttpExceptionFilter())) or the class (e.g., @UseFilters(HttpExceptionFilter)). The docs explain the trade-offs.

Here's what src/cats/cats.controller.ts should look like now:

import {
  Controller,
  Get,
  Post,
  Param,
  Body,
  Put,
  Delete,
  HttpStatus,
  HttpException,
  UseFilters,
} from '@nestjs/common';
import { CreateCatDto, UpdateCatDto } from './dto';
import { CatsService } from './cats.service';
import { Cat } from './interfaces/cat.interface';
import { HttpExceptionFilter } from '../common/filters/http-exception.filter';

@Controller('cats')
export class CatsController {
  constructor(private readonly catsService: CatsService) {}

  @Get('kaboom')
  badRoute() {
    throw new Error('kaboom');
  }

  @Post()
  async create(@Body() createCatDto: CreateCatDto) {
    this.catsService.create(createCatDto);
  }

  @Get()
  async findAll(): Promise<Cat[]> {
    return this.catsService.findAll();
  }

  // route to test our HttpExceptionFilter
  @Get('httpexcep')
  @UseFilters(HttpExceptionFilter)
  async getExcep() {
    throw new HttpException(
      {
        status: HttpStatus.FORBIDDEN,
        error: 'This is a custom message',
      },
      403
    );
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return `This action returns a #${id} cat`;
  }

  @Put(':id')
  update(@Param('id') id: string, @Body() updateCatDto: UpdateCatDto) {
    return `This action updates a #${id} cat`;
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return `This action removes a #${id} cat`;
  }
}

Continue to read through to the end of the chapter, but we've now completed the code changes for nest-cats. You can test it as described below.

Upon completion

You now have an exception handler defined for the GET /cats route. You can either proceed using your code, or if you've run into any issues, you can checkout the exception-filters-end branch to get caught up with the code as of the end of this chapter.

You should now be able to test the app with some REST requests.

HTTPie request demonstrating the exception filter

Test the exception filter by making a request to GET /cats with:

http GET :3000/cats/httpexcep

Notice that the HttpExceptionFilter is now handling this exception, replacing our custom exception message thrown in the route handler. The response should look like:

{
  "path": "/cats/httpexcep",
  "statusCode": 403,
  "timestamp": "2019-08-25T16:06:40.158Z"
}

What's next

Next up is the Pipes chapter.

Clone this wiki locally