18-Nodejs Course 2023: Break: Code Refactor
Hasan Zohdy
Posted on October 31, 2022
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;
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
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();
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
}
}
Let's remove the connection from the index.
// src/index.ts
import "app/routes";
import startApplication from "core/application";
import "./config";
startApplication();
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();
}
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
}
}
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;
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();
}
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;
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
- Event Driven Architecture: A Practical Guide in Javascript
- Best Practices For Case Styles: Camel, Pascal, Snake, and Kebab Case In Node And Javascript
- After 6 years of practicing MongoDB, Here are my thoughts on MongoDB vs MySQL
Packages & Libraries
- Collections: Your ultimate Javascript Arrays Manager
- Supportive Is: an elegant utility to check types of values in JavaScript
- Localization: An agnostic i18n package to manage localization in your project
React Js Packages
Courses (Articles)
Posted on October 31, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.