Modules in Node.js

By Anna Mykhailova on May. 6th, 2020

The modular system in Node.js allows developers to keep their code organized and clean, and promotes reusability of code and adoption of best practices across Node.js applications. In this guest blog post by Anna Mykhailova (@sunnyenotick), learn about the different types of modules, and how to use different modules in your own Node project.

If you are coming to Node.js from any other JavaScript framework or from a vanilla JS experience, you may find yourself in the midst of new terminology and concepts that you may not have heard of previously. One of these concepts is modules in Node.js. We are going to cover the concepts of modules in Node.js and give a beginner-friendly overview of this part of the Node.js framework.

In this blog post we will:

  • Understand the basics of the module concept
  • Learn about different types of modules
  • Learn about how to use different modules in a Node.js project

Goal

  • To learn about about modules, their benefits, and how they can be used inside your own Node.js application.

Prerequisites

  • None

What is a module?

Simply put, a module is a block of code that can be reused throughout an application. Even though Node.js uses modular architecture it doesn’t prevent you from putting the whole application code in one big index.js file. When I wrote my first JS application in vanilla JS -- way before Node.js was on the horizon -- I did, indeed, make that mistake and put all of my code in one JS script. Quickly, I realized that even though the code interpreter didn’t care, I as a human being did care about the organization, readability, and maintainability of my code.

Debugging quickly became quite difficult across 6,000+ lines of code, but the real challenge came when a teammate asked for a functionality I knew I had built, but it was just a small part of the entire app. I couldn’t simply share my code, nor could I easily reuse parts of it in the new app.

From a developer perspective, you may think about modules as collections of functions that belong together and provide certain functionality. Similar to JS libraries, they combine reusable features that later compose the application. Thinking of a real world analogy, we keep some food in the fridge, some in the freezer and some in the pantry, then we combine different foods to create dinner -- but it wouldn’t make sense to store all of it in one place.

So, modules help to keep code separated, clean and organized and, as a bonus, encourage collaboration within a coding community. People are able to release their modules for others to use, and we can plug in and use modules that fit our needs.

Node.js organizational structure

If you have experience with other frameworks, you may expect that in order to be considered a “module” the code needs to live in a certain folder, follow a certain naming convention or have some structure inside the code itself. However, this is not necessarily the case with Node.js. It treats every JS file as a module. For example, if you have a file named hello.js Node.js will think it’s a module named “hello”.

There are situations when one file is not enough to contain module functionality. In cases like that it is convenient to organize files into self-contained directories, and then provide a single entry point to those directories versus separate files. If you are not sure whether the directory is indeed a module, look for one of the following signs. First, check whether the directory has a package.json file and “main” entry in it pointing to a .js file inside that directory. Second, see if the directory has an index.js file in it. Lastly, check if potentially there is an index.node in the directory.

Working with modules

Generally speaking, in order to use modules you would need to plug them into your application or in other words require them in your code. This can be done with the require() function. The require function is like a gateway that allows our code to access code declared elsewhere. For example:

const http = require("http");

...will load the http module. After we require the module we are able to use its functionality. One of the advantages of this approach is that we can read about the module and its functions in the documentation. Using this method allows us to use the module without going deep into its code body. The minimum things we need to know about it would be its name, functions, and parameters it exposes.

Let's explore the various categories of modules in Node.js.

Built-in modules

Modules in Node.js are present by default. Even if you just created your first “Hello World” application you already have modules in it. This is because the Node.js environment provides access to core Node.js modules. These modules are built-in—meaning that they don’t need to be installed and are readily available to any Node.js projects. They are there as helpers that cover the most common reusable functionality so developers wouldn’t have to reinvent the wheel. If you ever looked into any Node.js tutorials or books you probably have already seen http module even though it might not have been obvious what it was at that time.

Built-in modules are the easiest to plug into your code, because they only need a name to be required. They are also well documented in the general Node.js documentation.

Let’s take a look at another built-in module: path. This module provides utilities for working with file and directory paths.

const path = require('path');

Now that we required the path module, we store it in the path constant and have access to all of its exposed methods and properties.

In order to use, for example, the parse() method (which transforms a string path into an object whose properties represent significant elements of the path: dir (directory), root, base, name, ext), we need to call this method on the object like so:

let pathObj =  path.parse(“/home/user/dir/example.txt”);

Contributed modules

As developers, we are not limited to just core modules, even though they help us to significantly simplify otherwise tedious tasks. Where Node.js really switches into a “turbo” mode is when you tap into collection of open source modules developed and maintained by the community.

Contributed modules cover different functionalities and needs—from libraries and frameworks to utility functions. Some of the most popular ones are webpack, moment, and express, among many others. These modules are packed and distributed as packages. Because those modules don’t come with any installation of Node.js—they are not built-in modules—it’s not enough to require() them in code. In order to use these modules they need to first be downloaded locally. This is a task for the Node Package Manager (npm) tool that comes bundled with Node.js by default.

For example, in order to connect to a popular distributed database that is commonly associated with Node.js, MongoDB, you would need to install it with npm (run this in your project folder):

npm install mongodb

Then it can be required in your application file—for example, if you’d like to add the connection to the DB client:

const MongoClient = require('mongodb').MongoClient;

Custom modules

Now that we’ve covered core and contributed modules, we know that we can achieve anything, or almost anything. There are always situations when a contributed module can’t provide the functionality we need in our application. That’s nothing to worry about, though, since we can write our own custom modules that fulfill our exact needs.

Custom modules can expose functionality for us to use in our code as well as hide parts of functionality that are irrelevant outside of the module.

Requiring custom modules is only slightly different from requiring built-in or contributed modules. In order to require them you pass the module’s file path into the require function.

As mentioned, Node.js would treat any file as a module by default. So in order to create your first module you would just need to create a new file.

Let’s go ahead and create a new file named myMealTime.js. We can check if the module is being required by simply adding a debug statement inside it:

console.log("Welcome to the Meal Time Module");

Now, it’s time to require it inside our main application index.js file:

const myMealTime = require("./myMealTime");

Notice how we passed the relative path ./ but still didn’t include the .js extension. The require function can find the file even without its extension. You can also use the extension when requiring, but it isn’t strictly necessary. If we run the app we will see that our new required module has logged the “Welcome to the Meal Time Module” string into our console. Right now, the module isn’t doing anything but running the code inside of it when it’s required.

To use the code in the module outside of the module itself, we will need to export code from the module, which we will do next.

Now, let’s add some functionality into the myMealTime module:

// Get current date.
let today = new Date();
// Allow other code to use getMealTime function.
module.exports.getMealTime = function() {
  let time = today.getHours();
  if (time > 16)
        return "It's dinner time!";
  else if (time > 12)
        return "It's lunch time!";
  else
        return "It's breakfast time!";
}

In order to let other modules or files use functions and parameters in our custom modules, our custom modules need to export them using module.exports.

Now we can use it in our index.js file like so:

console.log(myMealTime.getMealTime());

In this way we are able to use the function defined in our custom module inside our main application file. The module.exports code serves 2 purposes—it exposes code attributes we want to share and hides the ones we want to hide, as only exported parts of the code will be available for external use. This allows for clean and more encapsulated code that is easier to maintain in the future.

Recap

The modular system in Node.js allows developers to keep their code organized and clean, and promotes reusability of code and adoption of best practices across Node.js applications. Using modules ensures that you don’t repeat yourself, and allows you to use modules contributed by the community to your advantage.

There are three types of Node.js modules which are differentiated by their origin: Node.js core modules, community modules, and custom modules. All of them can be used inside your application with the help of the require() function.

Taking a modular approach to the code allows developers to control what functionality and parameters are exposed outside of the module, which is achieved using module.exports, and hide other business logic inside the module itself making it easier to plug into different parts of the application architecture.

Additional resources

Previous post:

No matter how many precautions you take, you can never assume a database is impenetrable. Because cybercriminals use an array of resources in cyber attacks, a key step in password security is salting and hashing. In this guest tutorial by Michelle Selzer (@codingCommander), learn how to salt and hash a password using bcrypt.

By Michelle Selzer on Apr. 28th, 2020
Next post:

By building a log to store exceptions and errors, we have a better chance of understanding bugs within our code as the users experience them. In this guest blog post by Luke Pate, learn how to write meaningful logs that explain your application and its activity to support future work by creating a simple Node API to capture requests and write exceptions to a logging file to be stored and viewed at a later time.

By Luke Pate on May. 6th, 2020