Welcome to this lesson on configuring middleware and data validation in NestJS. So far, we have covered the basics of integrating MongoDB into your NestJS application and transforming data store objects into Data Transfer Objects (DTOs
). These foundational steps have prepared you for building a functional ToDo app. Today, we'll take another important step by learning how to create and configure middlewares and add data validation in NestJS.
Middlewares play a crucial role in enterprise applications, providing functionalities like logging, authentication, and error handling. By the end of this lesson, you'll know how to create and integrate a timer middleware to monitor the performance of your application.
Validation, on the other hand, ensures that the data sent to your application meets predefined criteria. This is crucial for maintaining data integrity and security. By validating data, you can prevent common vulnerabilities, such as SQL injection and XSS attacks, and ensure that your application behaves as expected regardless of the input it receives.
A Middleware is a function that has access to the request and response objects and can modify them or end the request/response cycle. Let's create a timer middleware to log the duration of each request.
app/src/middlewares/timer.middleware.ts
TypeScript1import { Injectable, NestMiddleware } from '@nestjs/common'; 2import { Request, Response, NextFunction } from 'express'; 3 4@Injectable() 5export class TimerMiddleware implements NestMiddleware { 6 use(req: Request, res: Response, next: NextFunction) { 7 8 // Get the time we reveive the request 9 const start = Date.now(); 10 11 res.on('finish', () => { 12 // When the response finishes, determine how long it took to process the request and log it 13 const duration = Date.now() - start; 14 console.log(`${req.method} ${req.originalUrl} [${res.statusCode}] - ${duration}ms`); 15 }); 16 next(); 17 } 18}
- Imports: We import necessary classes and types from
@nestjs/common
andexpress
. @Injectable()
: Marks the class as a provider that can be injected into other parts of the application.- NestMiddleware Interface: Ensures the class adheres to the middleware structure in NestJS.
- use Method: Contains the logic to start a timer when the request is received and log the duration once the response is finished.
The use
method above is the core of the middleware function. It intercepts requests made to the server, allowing us to process or log information before the request moves to the next middleware or the route handler. In this code, it contains the logic to start a timer when the request is received and log the duration once the response is finished.
- req: This represents the incoming HTTP Request object. It contains information such as headers, the HTTP method (GET, POST, etc.), and the requested URL.
- res: This represents the Response object. It is used to send a response back to the client after processing the request.
- next: This is a callback function that tells NestJS to pass control to the next middleware or route handler. Without calling
next()
, the request would hang, as the server would not know that processing should continue.
Next, we need to register and integrate the timer middleware into our application.
app/src/app.module.ts
TypeScript1import { Module, MiddlewareConsumer, NestModule } from '@nestjs/common'; 2import { MongooseModule } from '@nestjs/mongoose'; 3import { TodoModule } from './todo/todo.module'; 4import { TimerMiddleware } from './middlewares/timer.middleware'; 5 6@Module({ 7 imports: [ 8 MongooseModule.forRoot('mongodb://localhost/nestjs-todo'), 9 TodoModule, 10 ], 11}) 12export class AppModule implements NestModule { 13 configure(consumer: MiddlewareConsumer) { 14 consumer 15 .apply(TimerMiddleware) 16 .forRoutes('*'); 17 } 18 }
- Imports: We import required modules like
MiddlewareConsumer
andNestModule
from@nestjs/common
- apply Method: Registers the
TimerMiddleware
. - forRoutes('*'): Applies the middleware to all routes in the application.
Run the script and check the console logs to see the timer middleware outputs, e.g.,
1POST /todos [201] - 15ms 2POST /todos [400] - 10ms 3GET /todos [200] - 5ms
In addition to middleware, validation is a key aspect of building reliable and secure enterprise applications. Let's add validation to the CreateTodoDto
to ensure that the data sent to your application meets certain criteria.
Bash1npm install class-validator
These libraries provide decorators and tools to validate and transform objects, which integrate seamlessly with NestJS. We'll install this for you.
app/src/dtos/todo.dto.ts
TypeScript1import { IsNotEmpty, IsString, IsOptional } from 'class-validator'; 2 3export class CreateTodoDto { 4 @IsNotEmpty() 5 @IsString() 6 title: string; 7 8 @IsOptional() 9 @IsString() 10 description?: string; 11}
- Imports: We import required decorators from
class-validator
. - @IsNotEmpty(): Ensures the field is not empty.
- @IsString(): Ensures the field is a string.
- @IsOptional(): Marks the field as optional.
app/src/todo/todo.controller.ts
TypeScript1@Post() 2async create(@Body(new ValidationPipe()) todo: CreateTodoDto): Promise<TodoDto> { 3 const newTodo = await this.todosService.createTodo(todo); 4 return transformTodoDto(newTodo); 5}
The ValidationPipe
in this context is used to automatically validate the incoming request's data against a DTO
(Data Transfer Object) before it reaches the controller's method.
- ValidationPipe: A built-in pipe provided by NestJS that can be used to validate the request data based on the rules defined in the
DTO
. - @Body(new ValidationPipe()): This decorator applies the
ValidationPipe
to thetodo
parameter, ensuring that the data conforms to the structure and validation rules specified in theCreateTodoDto
class before it proceeds to thecreate
method. - CreateTodoDto: This is the
DTO
that likely contains validation decorators like@IsString()
,@IsNotEmpty()
, etc., to enforce the required data format and constraints.
By using the ValidationPipe
, the controller method create
ensures that the todo
object is valid, adhering strictly to the defined schema in CreateTodoDto
. If the validation fails, an error response is sent back to the client automatically, and the method execution is halted. This helps in maintaining data integrity and preventing potential issues arising from malformed data.
In this lesson, we explored middleware and data validation in NestJS. We focused on creating and configuring a timer middleware as well as adding validation to our DTOs.
- Recap: Middlewares and Validation are crucial for intercepting and handling requests/responses, and validation ensures data integrity and security. We created a timer middleware to log request durations and added validation to our
CreateTodoDto
to ensure data meets the required criteria. - Practice: Now that you have learned these concepts, you will get hands-on practice with exercises provided after this lesson. This will solidify your understanding of configuring middleware and adding validation in NestJS.
- What's Next: In the upcoming lessons, we will delve deeper into other advanced topics such as advanced error handling, authentication, and authorization, which are essential for building robust enterprise applications.
Congratulations on making it this far. Let's move on to the exercises to reinforce your learning!