Welcome to this lesson on Error Handling in NestJS. So far, we've learned how to integrate MongoDB
, transform data store objects to DTOs
, and configure middleware in our NestJS application. Today, we'll focus on error handling, a critical aspect of building robust and user-friendly applications. Effective error handling ensures that our application can gracefully manage errors and provide meaningful feedback to users.
By the end of this lesson, you will be able to set up error handling for a NestJS application, create and use exception filters, and test various error scenarios to ensure your application handles errors as expected.
To handle errors effectively, NestJS provides a powerful mechanism called Exception Filters. These filters allow you to manage exceptions and provide custom error responses.
Now, let's move to the heart of our error handling approach: creating an Exception Filter.
Exception Filters in NestJS allow you to catch unhandled exceptions in your application and modify the response that users receive.
We'll walk through the process of creating an HttpExceptionFilter
.
Step 1: Import Required Modules
Create a new file named http-exception.filter.ts
and start by importing the necessary modules:
TypeScript1import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus } from '@nestjs/common'; 2import { Request, Response } from 'express';
Step 2: Define the HttpExceptionFilter
Class
Next, we define the exception filter class:
TypeScript1@Catch(HttpException) 2export class HttpExceptionFilter implements ExceptionFilter { 3 catch(exception: HttpException, host: ArgumentsHost) { 4 const ctx = host.switchToHttp(); 5 const response = ctx.getResponse<Response>(); 6 const request = ctx.getRequest<Request>(); 7 const status = exception.getStatus(); 8 9 const errorResponse = { 10 statusCode: status, 11 timestamp: new Date().toISOString(), 12 path: request.url, 13 }; 14 15 if (status === HttpStatus.INTERNAL_SERVER_ERROR) { 16 response.status(status).json(errorResponse); 17 } else { 18 response.status(status).json({ 19 ...errorResponse, 20 message: exception.message, 21 }); 22 } 23 } 24}
Explanation:
@Catch(HttpException)
decorator tells NestJS to use this filter forHttpExceptions
.- The
catch
method handles the exception, extracts the relevant details (status, request URL), and constructs a response. - Different responses for internal server errors and other types of errors ensure relevant information is sent back to the client.
Next, we'll integrate our HttpExceptionFilter
into the application.
Step 1: Modify app.module.ts
Edit app.module.ts
to add the filter:
TypeScript1import { Module, MiddlewareConsumer, NestModule } from '@nestjs/common'; 2import { APP_FILTER } from '@nestjs/core'; 3import { MongooseModule } from '@nestjs/mongoose'; 4import { TodoModule } from './todo/todo.module'; 5import { ValidationMiddleware } from './middlewares/validation.middleware'; 6import { TimerMiddleware } from './middlewares/timer.middleware'; 7import { HttpExceptionFilter } from './filters/http-exception.filter'; 8 9@Module({ 10 imports: [ 11 MongooseModule.forRoot('mongodb://localhost/nestjs-todo'), 12 TodoModule, 13 ], 14 providers: [ 15 { 16 provide: APP_FILTER, 17 useClass: HttpExceptionFilter, 18 } 19 ] 20}) 21export class AppModule implements NestModule { 22 configure(consumer: MiddlewareConsumer) { 23 consumer 24 .apply(TimerMiddleware) 25 .forRoutes('*'); 26 } 27}
Explanation:
- The
APP_FILTER
token from@nestjs/core
registers theHttpExceptionFilter
globally. - Middleware configurations ensure that
ValidationMiddleware
andTimerMiddleware
apply to specified routes.
To ensure our error handling works, we’ll simulate different errors. Create a send_request.ts
script with sample code:
TypeScript1import axios from 'axios'; 2 3// Create a new todo item 4async function createTodo() { 5 try { 6 const response = await axios.post('http://localhost:3000/todos', { 7 title: 'Learn NestJS', 8 description: 'Explore the basics of NestJS' 9 }); 10 return response.data; 11 } catch (error) { 12 if (error.response) { 13 console.log(`HTTP ${error.response.status}: `, error.response.data); 14 } 15 } 16} 17 18// Create a new todo item with invalid data 19async function createInvalidTodo() { 20 try { 21 await axios.post('http://localhost:3000/todos', { 22 title: '' // Invalid title 23 }); 24 } catch (error) { 25 if (error.response) { 26 console.log(`HTTP ${error.response.status}: `, error.response.data); 27 } 28 } 29} 30 31// Simulate internal server error 32async function simulateInternalServerError() { 33 try { 34 await axios.get('http://localhost:3000/error'); 35 } catch (error) { 36 if (error.response) { 37 console.log(`HTTP ${error.response.status}: `, error.response.data); 38 } 39 } 40} 41 42// Get all todo items 43async function getTodos() { 44 const response = await axios.get('http://localhost:3000/todos'); 45 console.log('Todos:', response.data); 46} 47 48async function run() { 49 try { 50 console.log('Running database migration'); 51 execSync('npm run typeorm migration:run'); 52 53 console.log('Creating a valid todo'); 54 await createTodo(); 55 56 console.log('Creating an invalid todo'); 57 await createInvalidTodo(); 58 59 console.log('Simulating internal server error'); 60 await simulateInternalServerError(); 61 62 console.log('Getting all todos'); 63 await getTodos(); 64 } catch (error) { 65 console.error('Error during run:', error.message); 66 } 67} 68 69run();
Explanation:
- Creating a valid ToDo item: Normal scenario; should return success.
- Creating an invalid ToDo item: Triggers validation error; ensures
ValidationMiddleware
andHttpExceptionFilter
work. - Simulating internal server error: Triggers server-side error handling; ensures server responses are customized.
- Getting all ToDo items: Verifies CRUD operations continue to function.
Expected output examples from running the script:
Bash1$ Creating a valid todo 2$ Creating an invalid todo 3$ HTTP 400: { statusCode: 400, timestamp: '2023-02-23T12:34:56.789Z', path: '/todos', message: 'Validation failed' } 4$ Simulating internal server error 5$ HTTP 500: { statusCode: 500, timestamp: '2023-02-23T12:34:56.789Z', path: '/todos/error' } 6$ Getting all todos 7$ Todos: [ ...list of todo items... ]
In this lesson, we've covered the essentials of error handling in NestJS. You learned how to:
- Set up error handling using Exception Filters.
- Create an
HttpExceptionFilter
to manage exceptions. - Integrate the filter into your application.
- Test various error scenarios to ensure robust error management.
Next, you'll get the chance to solidify your understanding through hands-on practice exercises. These exercises will involve creating error scenarios and verifying that your application handles them gracefully.
Congratulations on completing this course! You've taken significant steps toward mastering NestJS and building robust, enterprise-ready applications. Keep practicing and applying what you've learned, and you'll continue to grow as a developer. Happy coding!