DEV Community

Ilya R
Ilya R

Posted on

NestJS - Middleware

On the back-end, tasks are often set to check something before processing the request from the client or to add any fields before the response will be sent to the client. Middleware help us with this.


A Middleware is a function that is called before a route handler. Also, the Middleware can be called after the response is generated. Middlewares have access to the request and response objects. This function, as it were, intercepts the request/response and performs certain actions with it.

By default, NestJS middleware is similar to ExpressJS middleware.

Middleware functions can perform the following tasks:

  • execute any code.
  • make changes to the request and the response objects.
  • end the request-response cycle.
  • call the next middleware function in the stack.
  • if the current middleware function does not end the request-response cycle, it must call next() to pass control to the next middleware function. Otherwise, the request will be left hanging.

Custom proxies in NestJS are just a class wrapped in an @Injectable decorator. But, in this class, it is necessary to implement the NestMiddleware interface. There are no such requirements for functions.

import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class SimpleMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    // Your code...
    // Then call next function.
    next();
  }
}
Enter fullscreen mode Exit fullscreen mode

Middleware are registered in the configure method of the Module class. Modules that include Middleware must implement the NestModule interface.

import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { SimpleMiddleware } from './middleware/simple.middleware';
import { ProductsModule } from './cats/cats.module';

@Module({
  imports: [ProductsModule],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(SimpleMiddleware)
      .forRoutes('products');
  }
}
Enter fullscreen mode Exit fullscreen mode

This is how we can use our Middleware for specific routes:

configure(consumer: MiddlewareConsumer) {
  consumer
    .apply(SimpleMiddleware)
    .forRoutes({ path: 'products', method: RequestMethod.GET });
}
Enter fullscreen mode Exit fullscreen mode

The MiddlewareConsumer is a helper class. It provides several built-in methods to manage middleware. All of them can be simply chained in the fluent style. The forRoutes() method can take a single string, multiple strings, a RouteInfo object, a controller class and even multiple controller classes. In most cases you'll probably just pass a list of controllers separated by commas.

With the exclude method, we can exclude the roots that should not be applied to the Middleware

configure(consumer: MiddlewareConsumer) {
  consumer
    .apply(SimpleMiddleware)
    .exclude(
      { path: 'products', method: RequestMethod.POST }
    )
    .forRoutes(ProductsController);
}
Enter fullscreen mode Exit fullscreen mode

Sometimes we need to call multiple Middleware on specific routes.

In order to bind multiple middleware that are executed sequentially, simply provide a comma separated list inside the apply() method:

consumer.apply(cors(), helmet(), logger).forRoutes(ProductsController);
Enter fullscreen mode Exit fullscreen mode

I would also like to say about the possibility of using global Middlewares. To register them, we use the use method on the application instance.

const app = await NestFactory.create(AppModule);
// Some code
app.use(logger);
// Some code
Enter fullscreen mode Exit fullscreen mode

We usually use Middlewares to check the Api key, check the type of content in request, the presence of certain fields in the headers, user rights, etc.

Discussion (0)