How to create functional middleware for APIs in Nestjs?

February 3, 2023 - 6 min read

To create functional middleware for APIs in Nestjs, you need to do 3 things:

  • First, you need to create and export a function with 3 parameters to receive the request object, the response object, and the next function. In our case, we will be using the default Express middleware objects. The request object contains all the properties/methods to manipulate the incoming request, the response object contains all the properties/methods to manipulate the response for the request, and the next() function is used to notify and hand over the execution to other middlewares down the request/response lifecycle. This specific middleware is called functional because we are defining the middleware in the form of a function. These can also be created using a class-based approach.
  • Secondly, you need to register the newly created middleware using the apply() method of the MiddlewareConsumer interface and pass the middleware as its argument inside the configure() method of the feature module or root/core module class of the application.
  • Finally, you also need to set the routes for which the middleware needs to work upon by chaining the forRoutes() method after the apply() method and passing the route string or the Controller class.

TL;DR

Filepath: src/middlewares/application-logger.middleware.ts

// Express types for the req, res, and next variables
import { Request, Response, NextFunction } from "express";

// middleware to log the url and the host url of the incoming requests
export function ApplicationLogger(
  req: Request,
  res: Response,
  next: NextFunction
) {
  // log the url and the host url from the `req` object
  console.log({
    Host: req.host,
    URL: req.url,
  });

  next();
}

Filepath: src/app.module.ts

import { MiddlewareConsumer, Module, NestModule } from "@nestjs/common";
import { GreetController } from "./greet/greet.controller";
import { GreetService } from "./greet/greet.service";
import { ApplicationLogger } from "./middlewares/application-logger.middleware";

@Module({
  imports: [],
  controllers: [GreetController], // <- add controller here
  providers: [GreetService], // <- add provider here
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    // apply the `ApplicationLogger` middleware
    // to the `/greet` API endpoints using the `forRoutes()` method.
    consumer.apply(ApplicationLogger).forRoutes(GreetController);
  }
}

To understand it better, let's make a small middleware and use it for an API.

For example, let's say we need to create a logging middleware where we need to log the url and the hostname of the incoming request.

To do that, let's first create a functional middleware by creating and exporting a function called ApplicationLogger with 3 parameters namely req, res, and next. The req, res, and next variables are Express middleware object variables.

The types for the req, res object variables, and the next function can be imported from the express module which is already installed with Nestjs by default.

We will be creating the middleware inside the src/middlewares directory.

It can be done like this,

Filepath: src/middlewares/application-logger.middleware.ts

// Express types for the req, res, and next variables
import { Request, Response, NextFunction } from "express";

// middleware to log the url and the host url of the incoming requests
export function ApplicationLogger(
  req: Request,
  res: Response,
  next: NextFunction
) {}

Now inside the ApplicationLogger function, we can log the url and the host url from the req object variable using the console.log() method.

It can be done like this,

Filepath: src/middlewares/application-logger.middleware.ts

// Express types for the req, res, and next variables
import { Request, Response, NextFunction } from "express";

// middleware to log the url and the host url of the incoming requests
export function ApplicationLogger(
  req: Request,
  res: Response,
  next: NextFunction
) {
  // log the url and the host url from the `req` object
  console.log({
    Host: req.host,
    URL: req.url,
  });
}

Finally, to hand over the execution of the code to other middlewares/controllers down the request/response lifecycle, we can call the next() function.

It can be done like this,

Filepath: src/middlewares/application-logger.middleware.ts

// Express types for the req, res, and next variables
import { Request, Response, NextFunction } from "express";

// middleware to log the url and the host url of the incoming requests
export function ApplicationLogger(
  req: Request,
  res: Response,
  next: NextFunction
) {
  // log the url and the host url from the `req` object
  console.log({
    Host: req.host,
    URL: req.url,
  });

  next();
}

We have already made an API for the /greet - GET API.

To know more about creating a GET API, see the blog on How to make a simple GET request or an API endpoint in Nestjs?

Now we need to register the ApplicationLogger middleware for the /greet API endpoints. To do that, we need to use the apply() method from the MiddlewareConsumer interface and pass the ApplicationLogger middleware as an argument to the method inside the configure() of the root module of the application.

It can be done like this,

Filepath: src/app.module.ts

import { MiddlewareConsumer, Module, NestModule } from "@nestjs/common";
import { GreetController } from "./greet/greet.controller";
import { GreetService } from "./greet/greet.service";
import { ApplicationLogger } from "./middlewares/application-logger.middleware";

@Module({
  imports: [],
  controllers: [GreetController], // <- add controller here
  providers: [GreetService], // <- add provider here
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    // apply the `ApplicationLogger` middleware
    consumer.apply(ApplicationLogger);
  }
}

NOTE: If you look at the code above, you can see that the AppModule class implements the NestModule interface. This is needed for every module that is using any middleware.

We have applied the middlewares, but haven't told Nestjs which API endpoints we need to apply the middlewares to. To do that, we can chain the forRoutes() method to the apply() method and pass the GreetController class as an argument to it. This will instruct Nestjs that the ApplicationLogger will only need to be called for /greet API endpoints.

It can be done like this,

Filepath: src/app.module.ts

import { MiddlewareConsumer, Module, NestModule } from "@nestjs/common";
import { GreetController } from "./greet/greet.controller";
import { GreetService } from "./greet/greet.service";
import { ApplicationLogger } from "./middlewares/application-logger.middleware";

@Module({
  imports: [],
  controllers: [GreetController], // <- add controller here
  providers: [GreetService], // <- add provider here
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    // apply the `ApplicationLogger` middleware
    // to the `/greet` API endpoints using the `forRoutes()` method.
    consumer.apply(ApplicationLogger).forRoutes(GreetController);
  }
}

Now if you go to the /greet endpoint in the browser, you can see the Host and the URL of the incoming request being printed onto the terminal.

The terminal output might look similar to the below,

{ Host: '0y59ux-3000.preview.csb.app', URL: '/greet' }

We have successfully added the ApplicationLogger functional middleware for /greet API endpoints in Nestjs. Yay 🥳!

See the above code live in codesandbox.

That's all 😃.