arrow left
Back to Developer Education

How to Use Modular Patterns in Node.js

How to Use Modular Patterns in Node.js

In this article, we will learn in-detail what modularization of code is, and how to do it. We will also learn to modularize code in Node.js. When building applications, you don’t write all your code in one file. As this would be very difficult to manage. <!--more--> If it's a large application, it would be hard for a developer to come back to this code, refactor, or extend the application functionalities.

However, you can break down this complex application into smaller reusable parts.

Prerequisites

To follow this article along it will be useful to have the following:

Introduction

Typically, developers code is divided into several logical files. Each file has a different bit of code with certain functionality. This way, it is easier to maintain your codebase, because each file has less code to accomplish a specific task.

It is easier to debug and share the code with other developers, this makes it easier for application contributions and maintenance.

This concept is common in most programming languages. The concept is called modularity (Modular patterns). In this case, every single file is a module.

Let's say, for example, you are creating a calculator application. In this case, you can have a file that handles addition, and another file handling subtraction functionality. So, we have each file performing a specific arithmetical task.

Scope and namespace

This is a huge advantage for Node.js. When you use JavaScript without frameworks such as Node.js, there is no concept of public and private scope.

JavaScript offers local scope, which means the content of a function or an object is private, unless specifically exposed to any external module.

This might pollute the global namespace when executing this JavaScript code in the browser. Thus, you need to wrap the functions method in a function scope to enhance privacy and avoid populating the global namespace in the browser.

Node.js does not implement the global namespace. In Node.js, modular systems are implemented natively, based on the common JavaScript specifications.

This way, you implement a local namespace within every module. It contains variables, and properties that are private to a module until explicitly exposed/exported to other modules.

This breaks down the application complexity into manageable units and separating concerns.

Modularization

A modular system provides a scalable framework when building complicated apps.

These patterns can be implemented in several ways, but that depends on the modular programming paradigm of the host programming language.

In Node.js, the concept of Modular patterns has additional features that go beyond the regular module (file) pattern.

The Node.js modular functionality can be used to import functions, classes, objects, external files, core Node.js modules, and NPM modules.

A Node.js can be stateless or stateful:

  • Stateless expose stateless entities such as class, objects, and methods.
  • Stateful exposes a stateful instance of an object such as a database connection, and an instance of third-party API service.

This tutorial will focus on creating modules, exporting and importing them to different files using Node.js.

Let's create some Node.js modules.

Creating and importing a function module

To start with, create a project folder and include the app.js and main.js files respectively, as shown below:

// app.js
const sum = (num1,num2) => {
    return 'The sum is '+ (num1 + num2);
}
// main.js
const result = sum(10,14)
console.log(result);

It is clear that app.js wants to access function sum() from main.js.

When you run node main.js, it shows undefined. This is because sum is private to main.js. And cannot be accessible outside this module unless exposed publically to other modules.

This is how modules are made available when required by other modules.

// app.js
const sum = (num1, num2) => {
    return 'The sum is '+ (num1 + num2);
}

module.exports = sum 
// main.js
// search app.js in present directory
const sum = require('./app.js')

const result = sum(10, 14)
console.log(result); 

Here, we have a function sum() containing module.exports, which makes this function accessible outside of the module wherever we require that module.

The object module uses the property exports to expose the function sum natively. The function is now explicitly accessible and can now be invoked in other modules such as main.js.

To use this module, you need to import it to another module using require(). Here, we import the module app.js to module main.js.

Note: The require() function is synchronous. require() caches the value of module.exports and returns that same value for all future require() calls. If the module should return different value, you should export a function that can be called afterward to return a new value.

When you run node main.js, you'll get the results as expected.

What if you have different functions and export them to main.js.

This is how you would use the module system:

// app.js
const sum = (num1, num2) => {
    return 'The sum is '+ (num1 + num2);
}

const count = (anims) => {
    return 'The number of animals is '+ anims.length;
};

const greet = () => {
    console.log("Hello world");
}

module.exports = {
    sum:sum,
    count:count,
    greet:greet
}
// main.js
// search app.js in present directory
const myModule = require('./app.js')

const result = myModule.sum(10,14)
console.log(result); 

const animals = myModule.count(['Sheep','Horse','Tiger','Pigeon'])
console.log(animals);

myModule.greet();

In this case, module.exports is set to an object with a set of functions as properties you want to export.

The above patterns are called Revealing Module Pattern.

This is because you are only exposing the things you want to be public to other modules. It only exposes the final results when the require() is run on the other module.

Exporting and importing a class

// person.js
// constructor function for the Person class
function Person(name) {
    this.age = 21;
    this.name = name;
}
 
// export the class, so other modules can access Persion objects
module.exports = {
    Person: Person
}

This is a class Person containing objects name and age. Person is exposed as a property of module.exports. We can access the class Person by importing the person.js module to main.js, as shown below.

// main.js
// import the Person module
let persons = require('./person.js');
let Person = persons.Person;

// creates a Persion
let persons1 = new Person("John");

// find the names and ages persons1 in the class Persion
console.log(persons1.name + " is " + persons1.age + " old." );

In this case, we create an instance of class Person inside main.js using the new keyword.

Run node main.js to log the results in the console.

Exports

exports is a shorthand for module.exports.

exports helps you expose objects and methods.

Check this example to better understand:

//circle.js
// constant value
const pi = Math.PI

// using the the value to diggrent elements and exposing them to other modules
exports.areaOfaCircle = radiusOfaCircle => (radiusOfaCircle ** 2) * pi
exports.circumferenceOfaCircle = radiusOfaCircle => 2 * radiusOfaCircle * pi

The module circle exposes functions circumferenceOfaCircle and areaOfaCircle using alias exports.

To find the area and circumference of a circle you need a const PI. Math.PI provides this mathematical functionality.

In this case, const pi is private to circle.js. It's only accessible to this module.

We need a way to consume PI within this module and then expose its elements using exports.

Let's consume these elements as shown here:

//app.js
const circle = require('./app.js')

const r = 21
const area = circle.areaOfaCircle(r)
const circumference = circle.circumferenceOfaCircle(r)

console.log(`Area of the circle is: ${area}`)
console.log(`Circumference of the circle is: ${circumference}`)

Run node app.js to get the results.

module.exports vs exports

exports is just an alias variable used for the convenience of writing less code. exports behaves as if there is an implicit line of code that assigns it to module.exports i.e.:

const exports = module.exports = {};

This assignment occurs behind the scenes whenever you use exports.

Variable exports is only a pointer to the module.exports's initial value.

This is how you should use exports.

exports.areaOfaCircle = radiusOfaCircle => (radiusOfaCircle ** 2) * pi

Usecase

  • module.exports is commonly used to expose elements such as functions, objects, and classes at the root level.

For example:

module.exports = sum
  • If you'd rather have a single object that reveals multiple assignments, use the module.exports method.

For example:

module.exports = {
    sum:sum,
    count:count,
    greet:greet
}

Assigning more than one object to exports won't work

  • exports is great when directly exposing named functions such as exports.areaOfaCircle.

Using Node.js core modules

Node.js is bundled with a couple of modules such as HTTP file system (fs)

To use these modules in your project, you use the require() function to access an instance of modules.

For example, using Node.js HTTP module to create a simple server.

Node.js NPM modules

Npm provides open sources packages that you can use within your project.

When you install an npm package, dependencies are saved in a node_modules folder. For these modules to work, you need to import them using require().

To work with NPM modules, you have to run npm init -y to generate a package.json file.

Run npm install <package name> to make a package available for your project.

For example, Moment is an NPM module used when trying to display and format Node.js dates. Here is how you would use Moment.

Run npm install moment to install Moment dependencies for your project.

Using Moment to display today date.

//app.js
const moment = require('moment');
const todayDate = moment();

console.log(
    "The date today is " +
    todayDate.format('dddd, MMMM Do YYYY, h:mm:ss a')
);

Run node app.js, and we would get the output as shown below:

The date today is Saturday, May 1st 2021, 4:11:26 pm

Conclusion

Module patterns help you implement the art of building Node.js projects at scale by dividing them into reusable parts.

Happy coding.

Further reading


Peer Review Contributions by: Srishilesh P S

Published on: May 24, 2021
Updated on: Jul 12, 2024
CTA

Start your journey with Cloudzilla

With Cloudzilla, apps freely roam across a global cloud with unbeatable simplicity and cost efficiency
Get Started for Free