Learn Nest.js Middleware

What is it?

I view middleware as a layer of code that sits between a request and a response on a web server. Middleware can:

  • execute code
  • make changes to request and response objects
  • return a response
  • continue the request-response cycle by calling another middleware

How does it work in Nest.js?

Nest.js builds on top of Express. If you understand how to use middleware in Express you can apply many of those same concepts to Nest. You can implement Nest middleware as a class or in a function (also known as functional middleware). I almost always create my middleware in a class. Implementing in a class lets me organize all my middleware in a specific folder. Keeping middleware in classes feels a little closer to the single responsibility principle than having them in modules.

If you create your middleware in a class you will need to use the @Injectable() decorator. @Injectable() lets you use dependency injection to apply your middleware. Your middleware class will also need to implement the NestMiddleware.

If you decide to go the function route instead of creating a class Nest doesn’t have any special requirements. Be aware the functional middleware route may end up with a more complex structure as the project grows.

snowy road

Photo by Oliver Roos on Unsplash

I’ve included a common typescript example of using middleware for authorization below. This is the most common middleware use I’ve come across.

@Injectable()
export class AuthorizationMiddleware implements NestMiddleware{
   constructor(){}
   async use(req: Request, res: Response, nest: NextFunction){
      // get token and decode or any custom auth logic
      next()
   }
}

How to apply middleware

Apply middleware in any module file in your Nest project. The Nest.js docs use an example of applying middleware in your app module but you can apply middleware to any module. Let's take this basic app module and apply middleware to it:

import { Module } from '@nestjs/common';
import { FooController } from './foo.controller';
import { FooService } from './foo.service';

@Module({
  controllers: [FooController],
  providers: [FooService],
  exports: [FooService]
})
export class FooModule {} 

legos

Photo by Markus Spiske on Unsplash

First, we need to need to instantiate the NestModule interface. Don’t forget to import it as well.

import { Module, NestModule} from '@nestjs/common';
import { FooController } from './foo.controller';
import { FooService } from './foo.service';

@Module({
  controllers: [FooController],
  providers: [FooService],
  exports: [FooService]
})
export class FooModule implements NestModule{}

Next, we’ll use the configure function to set up our middleware. We’ll import the MiddlewareConsumer on this step and apply the AuthorizationMiddleware from earlier. It is also possible to make the configure function async.

import { Module, NestModule, MiddlewareConsumer} from '@nestjs/common';
import { FooController } from './foo.controller';
import { FooService } from './foo.service';
import { AuthorizationMiddleware } from './authorization.middleware'
@Module({
  controllers: [FooController],
  providers: [FooService],
  exports: [FooService]
})
export class FooModule implements NestModule{
   configure(userContext: MiddlewareConsumer){
      userContext.apply(AuthorizationMiddleware)
   }
}

Finally, we can also apply middleware to specific routes and request methods. The code below would only use the authorization middleware for a put request on the bar path. Don’t forget to import the RequestMethod enum. I’m only applying the middleware to one path but you can add an unlimited number of objects to the forRoutes() function.

import { Module, NestModule, MiddlewareConsumer} from '@nestjs/common';
import { FooController } from './foo.controller';
import { FooService } from './foo.service';
import { AuthorizationMiddleware } from './authorization.middleware'
@Module({
  controllers: [FooController],
  providers: [FooService],
  exports: [FooService]
})
export class FooModule implements NestModule{
   configure(userContext: MiddlewareConsumer){
      userContext
         .apply(AuthorizationMiddleware)
         .forRoutes({path: 'bar', method: RequestMethod.PUT})
   }
}

Conclusion

The above is a very basic step by step implementation of Nest middleware. The important part is to remember middleware takes a Request, Response and can return or call a next() function. If you’re looking to become a middleware master I would suggest getting your hands on a large Nest.js codebase, either enterprise or open-source and trying to implement some middleware. Authorization is a common middleware use case but I’ve also seen and implemented logging and timing middleware. Anything is possible but make sure that your code actually belongs in middleware.

Another helpful way to learn more is to read up on Express.js middleware, it is more robust than Nest middleware and having a solid understanding of Express is helpful in almost all aspects when writing Nest code. If this has been helpful please leave a clap and check out my blog Document Object for more helpful articles.