54-Nodejs Course 2023: Request Middleware
Tags: node, typescript, mongodb, fastify
Published: 2022-11-10 05:17:28

---
title: 54-Nodejs Course 2023: Request Middleware
published: true
description: 
series: Nodejs Course 2023
tags: nodejs, typescript, mongodb, fastify
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p9oa564ql6zesxtdoarw.png
# Use a ratio of 100:42 for best results.
---

We're still in the middle of events, but we need to introduce a new concept first before we move on as we need it in our request events, The concept is called `Middleware`.

## Middleware

Middleware is a function or list of functions that is executed before the request handler, it can be used to modify the request object, or to add some functionality to the request.

For example, we can use a middleware to check if the user is authenticated or not, or we can use it to check if the user has the required permissions to access the requested route, if not then we can stop the request and return a response error.

## Request Middleware

Request middleware is a middleware that is executed before the request handler, it can be used to modify the request object, or to add some functionality to the request, or stop the request and return a response error/success based on certain conditions.

## Request Middleware Implementation

Now let's start implementing the request middleware, if you recall, the router methods accepts two parameters, the first one is the route path, and the second one is the request handler, so we need to add a third parameter to the router methods, that one will be an options list, and we will add the `middleware` option to it.

Open `src/core/router/types.ts` and add let's update the Route type.

```ts
// src/core/router/types.ts
import { Request } from "core/http/request";

/**
 * Middleware method
 */
export type Middleware = (request: Request, response: any) => any;

export type RouteOptions = {
  /**
   * Route middleware
   */
  middleware?: Middleware[];
  /**
   * Route name
   */
  name?: string;
};

/**
 * Route Object
 */
export type Route = RouteOptions & {
  /**
   * Route method
   */
  method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
  /**
   * Route path
   */
  path: string;
  /**
   * Route handler
   */
  handler: any;
};
```

We defined the `Middleware` type, and we added the `middleware` option to the `RouteOptions` type.

Any middleware will receive the request object and the response object as parameters, if the middleware returns a value, then it will be used as the response, and the request handler and all other middlewares will not be executed.

Also we added a new option to the `RouteOptions` type, that one is the `name` option, it will be used to give a name to the route, we will use it later in the request events.

Finally we merged the `RouteOptions` type with the `Route` type.

Now let's update our Router class.

```ts
// src/core/router/index.ts
import request from "core/http/request";
import { Route, RouteOptions } from "./types";

export class Router {
  /**
   * Routes list
   */
  private routes: Route[] = [];

  /**
   * Router Instance
   */
  private static instance: Router;

  /**
   * Get router instance
   */
  public static getInstance() {
    if (!Router.instance) {
      Router.instance = new Router();
    }

    return Router.instance;
  }

  private constructor() {
    //
  }

  /**
   * Add get request method
   */
  public get(path: string, handler: any, options: RouteOptions = {}) {
    this.routes.push({
      method: "GET",
      path,
      handler,
      ...options,
    });

    return this;
  }

  /**
   * Add post request method
   */
  public post(path: string, handler: any, options: RouteOptions = {}) {
    this.routes.push({
      method: "POST",
      path,
      handler,
      ...options,
    });

    return this;
  }

  /**
   * Add put request method
   */
  public put(path: string, handler: any, options: RouteOptions = {}) {
    this.routes.push({
      method: "PUT",
      path,
      handler,
      ...options,
    });

    return this;
  }

  /**
   * Add delete request method
   */
  public delete(path: string, handler: any, options: RouteOptions = {}) {
    this.routes.push({
      method: "DELETE",
      path,
      handler,
      ...options,
    });

    return this;
  }

  /**
   * Add patch request method
   */
  public patch(path: string, handler: any, options: RouteOptions = {}) {
    this.routes.push({
      method: "PATCH",
      path,
      handler,
      ...options,
    });

    return this;
  }

  /**
   * Get all routes list
   */
  public list() {
    return this.routes;
  }

  /**
   * Register routes to the server
   */
  public scan(server: any) {
    this.routes.forEach(route => {
      const requestMethod = route.method.toLowerCase();
      const requestMethodFunction = server[requestMethod].bind(server);

      requestMethodFunction(route.path, this.handleRoute(route));
    });
  }

  /**
   * Handle the given route
   */
  private handleRoute(route: Route) {
    return async (fastifyRequest: any, fastifyResponse: any) => {
      request
        .setRequest(fastifyRequest)
        .setResponse(fastifyResponse)
        .setHandler(route.handler);

      return await request.execute();
    };
  }
}

const router = Router.getInstance();

export default router;
```

We added the `options` parameter to the router methods, and we merged it with the route object.

Now let's update the `handleRoute` method to receive the entire route object, and to call the `execute` method on the request object.

```ts
// src/core/router/index.ts
private handleRoute(route: Route) {
  return async (fastifyRequest: any, fastifyResponse: any) => {
    request
      .setRequest(fastifyRequest)
      .setResponse(fastifyResponse)
      .setRoute(route);

    return await request.execute();
  };
}
```

Now let's update our Request class to receive the route object instead of the handler.

```ts
// src/core/http/request.ts
import { Route } from "core/router/types";

export class Request {
  // ...
  
  /**
   * Route Object
   * 
   * Notice the `!` after the `Route` type, it means that the route is required.
   */
  private route!: Route;
  // remove the handler property
  
  /**
   * Set route handler
   */
  public setRoute(route: Route) {
    this.route = route;

    return this;
  }

  // remove the setHandler method
```

We replaced the `handler` property with the `route` property, and we added the `setRoute` method and removed the `setHandler` method.

Now let's update the `execute` method to call the middleware and the handler.

```ts
// src/core/http/request.ts

  /**
   * Execute the request
   */
  public async execute() {
    // check for middleware first
    if (this.route.middleware) {
      for (const middleware of this.route.middleware) {
        const output = await middleware(this, this.response);

        if (output !== undefined) {
          return output;
        }
      }
    }

    const handler = this.route.handler;

    // check for validation
    if (handler.validation) {
      if (handler.validation.rules) {
        const validator = new Validator(this, handler.validation.rules);

        try {
          await validator.scan(); // start scanning the rules
        } catch (error) {
          console.log(error);
        }

        if (validator.fails()) {
          const responseErrorsKey = config.get(
            "validation.keys.response",
            "errors",
          );
          const responseStatus = config.get("validation.responseStatus", 400);

          return this.response.status(responseStatus).send({
            [responseErrorsKey]: validator.errors(),
          });
        }
      }

      if (handler.validation.validate) {
        const result = await handler.validation.validate(this, this.response);

        if (result) {
          return result;
        }
      }
    }

    return await handler(this, this.response);
  }
```

In the execute method, we started by checking if the route has middleware, and if it does, we loop through the middleware and call each one of them, and if the middleware returns a value, we return it.

Then we check if the route has validation, and if it does, we check if the route has rules, and if it does, we create a new validator instance and we scan the rules, and if the validator fails, we return the errors.

Then we check if the route has a validation function, and if it does, we call it, and if it returns a value, we return it.

Finally, we call the handler and return the result.

And that's it!

## Usage

Now let's see how to use it in our application.

Open `src/app/users/route.ts` and update it to the following:

```ts
// src/app/users/route.ts
import router from "core/router";
import { Middleware } from "core/router/types";
import createUser from "./controllers/create-user";
import getUser from "./controllers/get-user";
import usersList from "./controllers/users-list";

const middleware1: Middleware = (request, response) => {
  console.log("middleware 1 is executed but not returning anything");
};

const middleware2: Middleware = (request, response) => {
  console.log(
    "middleware 2 is executed but returning a value that will stop the execution and return the value",
  );

  return {
    interrupted: true,
  };
};

router.get("/users", usersList);
router.get("/users/:id", getUser);
router.post("/users", createUser, {
  middleware: [middleware1, middleware2],
});
```

Now try to make a POST request to `/users`, it will not execute even the validation, because the middleware returned a value.

Also you'll see something like this in your console:

![Middleware](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0fpnbo85x7lly9k3efmf.png)

## 🎨 Conclusion

In this tutorial, we learned how to implement middleware in our application, and how to use it.

In our next article, we'll resume our events on requests.

## 🚀 Project Repository

You can find the latest updates of this project on [Github](https://github.com/hassanzohdy/nodejs-2023)

## 😍 Join our community

Join our community on [Discord](https://discord.gg/pb2vmdfhGf) to get help and support (Node Js 2023 Channel).

## 🎞️ Video Course (Arabic Voice)

If you want to learn this course in video format, you can find it on [Youtube](https://www.youtube.com/playlist?list=PLGO8ntvxgiZMJc7RN2lIq9WmMOlWZGzmz), the course is in Arabic language.

## 📚 Bonus Content 📚

You may have a look at these articles, it will definitely boost your knowledge and productivity.

General Topics

* [Event Driven Architecture: A Practical Guide in Javascript](https://dev.to/hassanzohdy/event-driven-architecture-the-best-paradigm-that-i-love-to-work-with-in-javascript-and-node-js-1gnk)
* [Best Practices For Case Styles: Camel, Pascal, Snake, and Kebab Case In Node And Javascript](https://dev.to/hassanzohdy/best-practices-for-case-styles-camel-pascal-snake-and-kebab-case-in-node-and-javascript-55oi)
* [After 6 years of practicing MongoDB, Here are my thoughts on MongoDB vs MySQL
](https://dev.to/hassanzohdy/after-6-years-of-practicing-mongodb-here-are-my-thoughts-on-mongodb-vs-mysql-574b)

Packages & Libraries

* [Collections: Your ultimate Javascript Arrays Manager](https://dev.to/hassanzohdy/collections-your-ultimate-javascript-array-handler-3o15)
* [Supportive Is: an elegant utility to check types of values in JavaScript](https://dev.to/hassanzohdy/supportive-is-an-elegant-utility-to-check-types-of-values-in-javascript-1b3e)
* [Localization: An agnostic i18n package to manage localization in your project](https://dev.to/hassanzohdy/mongez-localization-the-simplest-way-to-translate-your-website-regardless-your-favorite-framework-4gi3)

 React Js Packages

* [useFetcher: easiest way to fetch data in React Js](https://dev.to/hassanzohdy/usefetcher-the-easiest-way-to-fetch-data-in-react-45o9)

Courses (Articles)

* [React Js: Let"s Create File Manager With React Js and Node Js](https://dev.to/hassanzohdy/lets-create-a-file-manager-from-scratch-with-react-and-typescript-chapter-i-a-good-way-to-expand-your-experience-5g4k)