73-Nodejs Course 2023: Break IV: Refactoring Http Module
Tags: node, typescript, mongodb, fastify
Published: 2022-11-28 04:58:20

---
title: 73-Nodejs Course 2023: Break IV: Refactoring Http Module
published: true
description: 
series: Nodejs Course 2023
tags: nodejs, typescript, mongodb, fastify
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e6efax87ehlxspwn4yff.png
# Use a ratio of 100:42 for best results.
---

After our previous [Break III](https://dev.to/hassanzohdy/59-nodejs-course-2023-break-iii-enhancing-request-and-response-24k6) we have added new features, and enhanced our existing onces, we added Middleware concept, and made our application a little more secure using [throttling](https://dev.to/hassanzohdy/71-nodejs-course-2023-request-throttle-rate-limiters-and-ddos-attacks-2gkk) and [Bloody Cors](https://dev.to/hassanzohdy/72-nodejs-course-2023-the-bloody-cors-2lj9), we also created our new [Auth](https://dev.to/hassanzohdy/60-nodejs-course-2023-auth-introduction-2o5h) module and worked with [JWT](https://dev.to/hassanzohdy/61-nodejs-course-2023-auth-jwt-2fma).

So we've done a lot of work here since then, now we need to clean up our code.

## 📜 The Plan

Our `http` folder is a total mess, we have a lot of files, and we need to clean it up, we need to move some files and folders, so here what we are going to do:

* Updating `index.ts` to encapsulate everything to be exported from it.
* Renaming `connectToServer` function to `createHttpApplication` which makes more sense.
* Updating our `types` file to define our `httpConfigurations` interface.
* Creating a new internal config that includes default configurations and an `httpConfig` function to quickly return a configuration from `http.*` configurations.

That's in my head right now, so let's start.

## 📝 Updating `index.ts`

Open `src/core/http/index.ts` and update it to look like this:

```ts
// src/core/http/index.ts
export { default as connectToServer } from "./connectToServer";
// request exports
export * from "./request";
// we need to make a default export from request. ts but to be
// exported as request not as default export
export { default as request } from "./request";
// response exports
export * from "./response";
// same thing like request
export { default as response } from "./response";
// server exports, but not recommended to beb used outside this folder
export * from "./server";
// types
export * from "./types";
// Uploaded file
export { default as UploadedFile } from "./UploadedFile";
```

Nothing fancy, we just exported all of our internal files to be exported from one place, now let's rename our main function.

## 📝 Renaming `connectToServer` function

As i said earlier, `connectToServer` function name doesn't make sense, so let's rename it to `createHttpApplication` which makes more sense.

Open `src/core/http/connectToServer.ts` and rename it to `createHttpApplication.ts` and update it to look like this:

```ts
// src/core/http/createHttpApplication.ts
import config from "@mongez/config";
import router from "core/router";
import registerHttpPlugins from "./plugins";
import response from "./response";
import { getServer } from "./server";

export default async function createHttpApplication() {
  const server = getServer();

  await registerHttpPlugins();

  // call reset method on response object to response its state
  server.addHook("onResponse", response.reset.bind(response));

  router.scan(server);

  try {
    // 👇🏻 We can use the url of the server
    const address = await server.listen({
      port: config.get("app.port"),
      host: config.get("app.baseUrl"),
    });

    console.log(`Start browsing using ${address}`);
  } catch (err) {
    console.log(err);

    server.log.error(err);
    process.exit(1); // stop the process, exit with error
  }
}
```

Now update it in our `index.ts` file:

```ts
// src/core/http/index.ts
export { default as createHttpApplication } from "./createHttpApplication";
// ...
```

Now let's update our `src/core/application.ts` to use our new function:

```ts
// src/core/application.ts
import { connectToDatabase } from "core/database";
import { createHttpApplication } from "core/http";

export default async function startApplication() {
  connectToDatabase();
  createHttpApplication();
}
```

Simple enough, right?

Now let's update all of our routes that we used a direct import for request and response types.

```ts
// src/app/users/controllers/create-user.ts
import { Request } from "core/http";

export default async function createUser(request: Request) {
  const { name, email } = request.body;

  return {
    name,
    email,
  };
}

createUser.validation = {
  rules: {
    name: ["required", "string"],
    email: ["required", "string"],
  },
  validate: async () => {
    //
  },
};
```

We replaced `import { Request } from 'core/http/request'` to `import { Request } from 'core/http'` which is the same thing.

Now go through all your files and update it as i did above.

> You can easily find it in your IDE by searching for `core/http/` and it will show you all files that uses request and response imports.

Also if you found something like this `import request from 'core/http/request'` just replace it with `import { request } from 'core/http'`.

## 📝 Updating our `types` file

Now let's add our `httpConfigurations` interface to our `types` file.

> I'm going to use `interface` this time just so you can be familiar with both, i prefer `type` though but it's up to you.

```ts
// src/core/http/types.ts
// ...

/**
 * Http Configurations list
 */
export interface HttpConfigurations {
  /**
   * Http middlewares list
   */
  middleware?: {
    /**
     * All middlewares that are passed to `all` array will be applied to all routes
     */
    all?: Middleware[];
    /**
     * Middlewares that are passed to `only` object will be applied to specific routes
     */
    only?: {
      /**
       * Routes list
       * @example routes: ["/users", "/posts"]
       */
      routes?: string[];
      /**
       * Named routes list
       *
       * @example namedRoutes: ["users.list", "posts.list"]
       */
      namedRoutes?: string[];
      /**
       * Middlewares list
       */
      middleware: Middleware[];
    };
    /**
     * Middlewares that are passed to `except` object will be excluded from specific routes
     */
    except?: {
      /**
       * Routes list
       * @example routes: ["/users", "/posts"]
       */
      routes?: string[];
      /**
       * Named routes list
       *
       * @example namedRoutes: ["users.list", "posts.list"]
       */
      namedRoutes?: string[];
      /**
       * Middlewares list
       */
      middleware: Middleware[];
    };
  };
}
```

As you can tell from above, we just defined what we literally have in our `http` configurations.

> Note that in the `only` and `except` the `middleware` property is required, so you can't just pass an empty object.

Also, if you notice that `only` and `except` has the same definition, so let's create an interface for it.

```ts
// src/core/http/types.ts
// ...

/**
 * Partial Middleware
 */
export interface PartialMiddleware {
  /**
   * Routes list
   * @example routes: ["/users", "/posts"]
   */
  routes?: string[];
  /**
   * Named routes list
   *
   * @example namedRoutes: ["users.list", "posts.list"]
   */
  namedRoutes?: string[];
  /**
   * Middlewares list
   */
  middleware: Middleware[];
}

/**
 * Http Configurations list
 */
export interface HttpConfigurations {
  /**
   * Http middlewares list
   */
  middleware?: {
    /**
     * All middlewares that are passed to `all` array will be applied to all routes
     */
    all?: Middleware[];
    /**
     * Middlewares that are passed to `only` object will be applied to specific routes
     */
    only?: PartialMiddleware;
    /**
     * Middlewares that are passed to `except` object will be excluded from specific routes
     */
    except?: PartialMiddleware;
  };
}
```

Now our code is much cleaner and easier to maintain later.

## 📝 Creating internal `config` file

The purpose of this file is to easily manage our `http` configurations, so we can get the http config and its default value directly.

```ts
// src/core/http/config.ts
import { HttpConfigurations } from "./types";

/**
 * Default http configurations
 */
export const defaultHttpConfigurations: HttpConfigurations = {
  // 
};
```

We added just an empty object for now, but what configurations should be added more besides the `middleware`? Let's see.

An easy and quick way to do so, is Right click on the `http` directory and click on `Find in Folder` and search for `config.get` and it will show you all files that uses the `http` configurations.

Which are the following:

* `app.port`: we'll replace it to be `http.port` as it is just related to the http application, (For example sockets have their own port)
* `app.baseUrl`: this one will also be replaced to `http.baseUrl` as it is just related to the http application.
* `http.middleware.all`: our new middleware configurations that collects all middlewares that are applied to all routes.
* `http.middleware.only`: our new middleware configurations that collects all middlewares that are applied to specific routes.
* `http.middleware.except`: our new middleware configurations that collects all middlewares that are excluded from specific routes.

So we've here 4 configurations that we need to add to our `defaultHttpConfigurations` object.

We added the middleware in http configurations type, let's add the `port` type

```ts
// src/core/http/types.ts
// ...

/**
 * Http Configurations list
 */
export interface HttpConfigurations {
  /**
   * Server port
   */
  port?: number;
  /**
   * Host
   */
  host?: string;
  /**
   * Http middlewares list
   */
  middleware?: {
    /**
     * All middlewares that are passed to `all` array will be applied to all routes
     */
    all?: Middleware[];
    /**
     * Middlewares that are passed to `only` object will be applied to specific routes
     */
    only?: PartialMiddleware;
    /**
     * Middlewares that are passed to `except` object will be excluded from specific routes
     */
    except?: PartialMiddleware;
  };
}
```

Now let's add the `port` to our `defaultHttpConfigurations` object to be default to `3000` and the `host` to be default to `0.0.0.0`.

> Why would we set host to `0.0.0.0`? based on [Fastify documentation](https://www.fastify.io/docs/latest/Guides/Getting-Started/#your-first-server) it can be used to listen to any host if we don't want to specify a specific host.

```ts
// src/core/http/config.ts
import { HttpConfigurations } from "./types";

/**
 * Default http configurations
 */
export const defaultHttpConfigurations: HttpConfigurations = {
  port: 3000,  
  host: "0.0.0.0",
  middleware: {
    all: [],
    only: {
      middleware: [],
    },
    except: {
      middleware: [],
    },
  },
};
```

I listed here the port, and all the middlewares as empty arrays.

## 📝 Creating the `httpConfig` function

Now we have our `defaultHttpConfigurations` object, let's create a function that will return the `http` configurations.

```ts
// src/core/http/config.ts
import config from "@mongez/config";
import { get } from "@mongez/reinforcements";
import { HttpConfigurations } from "./types";

/**
 * Default http configurations
 */
export const defaultHttpConfigurations: HttpConfigurations = {
  port: 3000,
  middleware: {
    all: [],
    only: {
      middleware: [],
    },
    except: {
      middleware: [],
    },
  },
};

/**
 * Get http configurations for the given key
 */
export function httpConfig(key: string): any {
  return config.get(`http.${key}`, get(defaultHttpConfigurations, key));
}
```

Here we used the `config.get` function to get the `http` configurations, and if it doesn't exist, we'll return the default value from the `defaultHttpConfigurations` using `get` utility to return a nested key's value.

## 📝 Using the `httpConfig` function

Now let's update our code to use our new `httpConfig` function.

```ts
// src/core/http/request.ts
import { httpConfig } from "./config";
//...

  /**
   * Collect middlewares for current route
   */
  protected collectMiddlewares(): Middleware[] {
    // we'll collect middlewares from 4 places
    // We'll collect from http configurations under `http.middleware` config
    // it has 3 middlewares types, `all` `only` and `except`
    // and the final one will be the middlewares in the route itself
    // so the order of collecting and executing will be: `all` `only` `except` and `route`
    const middlewaresList: Middleware[] = [];

    // 1- collect all middlewares as they will be executed first 👇🏻 we used our new `httpConfig` function
    const allMiddlewaresConfigurations = httpConfig("middleware.all");

    // check if it has middleware list
    if (allMiddlewaresConfigurations?.middleware) {
      // now just push everything there
      middlewaresList.push(...allMiddlewaresConfigurations.middleware);
    }

    // 2- check if there is `only` property 👇🏻 we used our new `httpConfig` function
    const onlyMiddlewaresConfigurations = httpConfig("middleware.only");

    if (onlyMiddlewaresConfigurations?.middleware) {
      // check if current route exists in the `routes` property
      // or the route has a name and exists in `namedRoutes` property
      if (
        onlyMiddlewaresConfigurations.routes?.includes(this.route.path) ||
        (this.route.name &&
          onlyMiddlewaresConfigurations.namedRoutes?.includes(this.route.name))
      ) {
        middlewaresList.push(...onlyMiddlewaresConfigurations.middleware);
      }
    }

    // 3- collect routes from except middlewares 👇🏻 we used our new `httpConfig` function
    const exceptMiddlewaresConfigurations = httpConfig("middleware.except");

    if (exceptMiddlewaresConfigurations?.middleware) {
      // first check if there is `routes` property and route path is not listed there
      // then check if route has name and that name is not listed in `namedRoutes` property
      if (
        !exceptMiddlewaresConfigurations.routes?.includes(this.route.path) &&
        this.route.name &&
        !exceptMiddlewaresConfigurations.namedRoutes?.includes(this.route.name)
      ) {
        middlewaresList.push(...exceptMiddlewaresConfigurations.middleware);
      }
    }

    // 4- collect routes from route middlewares
    if (this.route.middleware) {
      middlewaresList.push(...this.route.middleware);
    }

    return middlewaresList;
  }
```

We just used our new `httpConfig` function to get the `http` configurations instead of using the `config` handler directly, this will make our code more readable and easier to maintain.

Now let's update our `createHttpApplication` function to use our new `httpConfig` function.

```ts
// src/core/http/createHttpApplication.ts
import router from "core/router";
import { httpConfig } from "./config";
import registerHttpPlugins from "./plugins";
import response from "./response";
import { getServer } from "./server";

export default async function createHttpApplication() {
  const server = getServer();

  await registerHttpPlugins();

  // call reset method on response object to response its state
  server.addHook("onResponse", response.reset.bind(response));

  router.scan(server);

  try {
    // 👇🏻 We can use the url of the server
    const address = await server.listen({
      port: httpConfig("port"),
      host: httpConfig("host"),
    });

    console.log(`Start browsing using ${address}`);
  } catch (err) {
    console.log(err);

    server.log.error(err);
    process.exit(1); // stop the process, exit with error
  }
}
```

We just used our new `httpConfig` function to get the `http` configurations instead of using the `config` handler directly, this will make our code more readable and easier to maintain.

Now we need to move our `app.port` and `app.baseUrl` to  the `http` configurations.

```ts
// src/config/http.ts
import { env } from "@mongez/dotenv";
import { authMiddleware } from "core/auth/auth-middleware";
import { HttpConfigurations } from "core/http";

const httpConfigurations: HttpConfigurations = {
  port: env("PORT", 3000),
  host: env("HOST", "localhost"),
  middleware: {
    // apply the middleware to all routes
    all: [],
    // apply the middleware to specific routes
    only: {
      routes: [],
      namedRoutes: ["users.list"],
      middleware: [authMiddleware("guest")],
    },
    // exclude the middleware from specific routes
    except: {
      routes: [],
      namedRoutes: [],
      middleware: [],
    },
  },
};

export default httpConfigurations;
```

Now our `app.ts` config file has become very simple.

```ts
// src/config/app.ts
import { env } from "@mongez/dotenv";

const appConfigurations = {
  debug: env("DEBUG", false),
};

export default appConfigurations;
```

Final thing to update is our `.env` file to replace `BASE_URL` with `HOST`

```env
# App Configurations
DEBUG=true
APP_NAME="My App"

# Http Configurations
PORT=3000
HOST=localhost

# Database Configurations
DB_HOST=localhost
DB_PORT=27017
DB_NAME=ninjaNode
DB_USERNAME=root
DB_PASSWORD=root
```

> Writing comments inside a file will help you understand and more importantly, remember what are these keys will be going to used for.

## 🎨 Conclusion

Wheoh, we've made such a brilliant cleanup here, the http module is now much better, we can refine it more though 😜 but we're good for now.

I hope you enjoyed this article, and I hope you learned something new, if you have any questions or suggestions, please let me know in the comments below.

One final note, we may do such a repeated code refactoring to the same module as much as we enhance it and add new features, this is a healthy thing to your codebase.

## ☕♨️ Buy me a Coffee ♨️☕

If you enjoy my articles and see it useful to you, you may [buy me a coffee](https://www.paypal.com/paypalme/hassanzohdy), it will help me to keep going and keep creating more content.

## 🚀 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)

* [OOP In JS And TS From The Very Beginning](https://dev.to/hassanzohdy/jsts-oop-course-1-introduction-2ld)
* [ES6++: The Ultimate Guide to ES6 and Beyond](https://dev.to/hassanzohdy/es6-1-introduction-h3e)
* [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)