DEV Community

Hasan Zohdy
Hasan Zohdy

Posted on • Updated on

18-Nodejs Course 2023: Break: Code Refactor

Now our code started to be more complex, we need to enhance it more little bit.

Let's start with out database folder.

Database Folder

We have so far two files, connection.ts which contains the connection class and database class which contains the database manager, but when we import them they are imported from their own files, let's wrap it in an index file.

// src/core/database/index.ts
import database from "./database";

export { Connection, default as connection } from "./connection";
export { Database } from "./database";
export default database;
Enter fullscreen mode Exit fullscreen mode

So we export the connection object and its class, the database class and the database object.

But the only difference is that we exported the database as default, so we can import it like this:

import database from "core/database";

// do something with database
Enter fullscreen mode Exit fullscreen mode

Now we can import them from the index file.

// src/index
import "app/routes";
import startApplication from "core/application";

// ❌ replace  this
import connection from "core/database/connection";
// ✅ with this
import { connection } from "core/database";

import "./config";

startApplication();

connection.connect();
Enter fullscreen mode Exit fullscreen mode

Moving the connection

Now let's move our database connection into the startApplication function.

// src/core/application.ts
import multipart from "@fastify/multipart";
import Fastify from "fastify";
import { connection } from "./database";
import router from "./router";

export default async function startApplication() {
  const server = Fastify();

  server.register(multipart, {
    attachFieldsToBody: true,
  });

  router.scan(server);

 // connect to database
  connection.connect();

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

    console.log(`Start browsing using ${address}`);
  } catch (err) {
    server.log.error(err);
    process.exit(1); // stop the process, exit with error
  }
}
Enter fullscreen mode Exit fullscreen mode

Let's remove the connection from the index.

// src/index.ts
import "app/routes";
import startApplication from "core/application";
import "./config";

startApplication();
Enter fullscreen mode Exit fullscreen mode

We shall see the same, nothing changed.

More Enhancements of startApplication

If we can see, there are two main functions that we can extract from the startApplication function, the first one is to connect to database and the other one is for requests handling, so let's split it into two functions.

import multipart from "@fastify/multipart";
import Fastify from "fastify";
import { connection } from "./database";
import router from "./router";

function connectToDatabase() {
  connection.connect();
}

async function connectToServer() {
  const server = Fastify();

  server.register(multipart, {
    attachFieldsToBody: true,
  });

  router.scan(server);

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

    console.log(`Start browsing using ${address}`);
  } catch (err) {
    server.log.error(err);
    process.exit(1); // stop the process, exit with error
  }
}

export default async function startApplication() {
  connectToDatabase();
  connectToServer();
}
Enter fullscreen mode Exit fullscreen mode

This is much easier to maintain and read, also if we want to add more options in the start application it won't be too complicated.

Want more enhancement? let's make a new directory called requests and move the connectToServer function into it.

// src/core/requests/index.ts
import multipart from "@fastify/multipart";
import Fastify from "fastify";
import router from "core/router";

export default async function startServer() {
  const server = Fastify();

  server.register(multipart, {
    attachFieldsToBody: true,
  });

  router.scan(server);

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

    console.log(`Start browsing using ${address}`);
  } catch (err) {
    server.log.error(err);
    process.exit(1); // stop the process, exit with error
  }
}
Enter fullscreen mode Exit fullscreen mode

And let's also move the connectToDatabase function into the database folder.

// src/core/database/index.ts
import connection from "./connection";
import database from "./database";

export function connectToDatabase() {
  connection.connect();
}

export { Connection } from "./connection";
export { Database } from "./database";
export default database;
Enter fullscreen mode Exit fullscreen mode

Now let's update our startApplication function.

// src/core/application.ts
import { connectToDatabase } from "core/database";
import startServer from "core/requests";

export default async function startApplication() {
  connectToDatabase();
  startServer();
}
Enter fullscreen mode Exit fullscreen mode

Pretty neat, right?

Update connection class

Now let's update our connection class, we can split the database configuration in a method for example.

// src/core/database/connection.ts
import config from "@mongez/config";
import chalk from "chalk";
import { MongoClient } from "mongodb";
import database, { Database } from "./database";

export class Connection {
  /**
   * Mongo Client
   */
  public client?: MongoClient;

  /**
   * Database instance
   */
  public database?: Database;

  /**
   * Connect to the database
   */
  public async connect() {
    const { host, port, username, password, databaseName } =
      this.configurations;

    try {
      this.client = await MongoClient.connect(`mongodb://${host}:${port}`, {
        auth: {
          username: username,
          password: password,
        },
      });

      const mongoDBDatabase = await this.client.db(databaseName);

      this.database = database.setDatabase(mongoDBDatabase);

      console.log(
        chalk.green("Connected!"),
        !username || !password
          ? chalk.red("Your not making a secure authenticated connection!")
          : "",
      );
    } catch (error) {
      console.log(error);
    }
  }

  /**
   * Get database configurations
   */
  public get configurations() {
    return {
      host: config.get("database.host", "localhost"),
      port: config.get("database.port", 27017),
      username: config.get("database.username", ""),
      password: config.get("database.password", ""),
      databaseName: config.get("database.name", ""),
    };
  }
}

const connection = new Connection();

export default connection;
Enter fullscreen mode Exit fullscreen mode

We just moved the configurations list into a method, this also will allow us to get the configurations from anywhere in the application later.

Please note that the configurations is a getter method, so we can't call it like a function, we have to call it like a property.

Conclusion

This practice should be in your regular development, it MUST BE NOT over-engineering, it's just a good practice to make your code more readable and maintainable from time to time.

🎨 Project Repository

You can find the latest updates of this project on Github

😍 Join our community

Join our community on Discord 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, 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

Packages & Libraries

React Js Packages

Courses (Articles)

Top comments (0)