Node.js Modules

Summary: in this tutorial, you will learn about Node.js modules and understand how they work.

Introduction to the Node.js modules

Node.js supports a module system called CommonJS modules by default. Later, it supports ES modules starting in version 14.0.0. This tutorial focuses on the CommonJS modules.

In CommonJS modules, Node.js treats each JavaScript file as a separate module.

Let’s take a simple example to see how modules work in Node.js.

Creating a new module

First, create a new file called logger.js with the following code:

const error = 'ERROR';
const warning = 'WARNING';
const info = 'INFO';

function log(message, level = info) {
    console.log(`${level}: ${message}`);
}
Code language: JavaScript (javascript)

The logger.js contains three constants and a function.

The log() function in the logger module accepts two parameters: message and level. If you don’t pass the level into the log() function, it defaults to info.

In the logger.js module, all the variables and functions are private. This means that they are invisible and cannot be used in other modules.

To use the variables and functions of a module in another module, you need to export them at the end of the logger.js file:

module.exports.log = log;
module.exports.error = error;
module.exports.info = info;
module.exports.warning = warning;Code language: JavaScript (javascript)

This code creates new properties on the module.exports object and assigns them to the corresponding variables and functions.

Also, you can use different names when exporting the objects. For example:

module.exports.fatal = error;Code language: JavaScript (javascript)

In this case, other modules will reference the error constant as the fatal constant.

Now, you’re ready to use the log() function and all the constants of the logger module in another module.

Importing a module

Second, create a new file called app.js that uses the logger.js module. To use the logger.js module from the app.js, you need to import the logger module by using the require() function:

const logger = require('./logger.js');Code language: JavaScript (javascript)

Or you can remove the .js extension from the logger.js like this:

const logger = require('./logger');Code language: JavaScript (javascript)

Behind the scenes, the require() function executes the logger.js file and returns the exports object. If the require() function cannot find the file, it’ll throw an error.

The following shows the logger objects to the console:

const logger = require('./logger');

console.log(logger);Code language: JavaScript (javascript)

Output:

{
    log: [Function: log],
    error: 'ERROR',      
    info: 'INFO',        
    warning: 'WARNING'   
}Code language: JavaScript (javascript)

The logger object contains the log() function and other constants from the logger.js module. You can reference them as follows:

const logger = require('./logger');

logger.log('Node.js module demo 1');
logger.log('Node.js module demo 2', logger.warning);Code language: JavaScript (javascript)

Output:

INFO: Node.js module demo 1   
WARNING: Node.js module demo 2Code language: plaintext (plaintext)

To make the code more concise, you can use the object destructuring when importing a module like this:

const { log, error, info, warning } = require('./logger');Code language: JavaScript (javascript)

The object destructuring assigns the properties of the exports object returned by the require() function to the variables on the left side.

After destructuring, you can these variables directly:

log('Node.js module demo 1');
log('Node.js module demo 2', warning);Code language: JavaScript (javascript)

Output:

INFO: Node.js module demo 1   
WARNING: Node.js module demo 2Code language: JavaScript (javascript)

Understanding the module wrapper function

Before Node.js executes a module, it wraps all the code inside that module with a function wrapper which looks like the following:

(function(exports, require, module, __filename, __dirname) {
    // Module code
});Code language: JavaScript (javascript)

For example, the code of the logger.js module before execution will look like this:

(function (exports, require, module, __filename, __dirname) {
    const error = 'ERROR';
    const warning = 'WARNING';
    const info = 'INFO';

    function log(message, level = info) {
        console.log(`${level}: ${message}`);
    }

    module.exports.log = log;
    module.exports.error = error;
    module.exports.info = info;
    module.exports.warning = warning;
});Code language: JavaScript (javascript)

By doing this, Node.js achieves the following important goals:

  • Keep the top-level variables (var, let, and const) scoped to the module instead of the global object.
  • Make some module-specific variables like global variables for example module and exports.

Notice that the exports object references the module.exports:

console.log(module.exports === exports); // trueCode language: JavaScript (javascript)

Importing the same module multiple times

When you use the require() function to include a module multiple times, the require() function evaluates the module once only at the first call and puts it in a cache.

From the subsequent calls, the require() function uses the exports object from the cache instead of executing the module again.

The following example illustrates how it works:

First, create a new module called dblogger.js that has the following code:

console.log('Connected to the DB');Code language: JavaScript (javascript)

Second, use the require() function to include the dblogger.js module multiple times in the app.js:

let dbLogger = require('./dblogger');
dbLogger = require('./dblogger');Code language: JavaScript (javascript)

Output:

DBLogger is loaded.Code language: JavaScript (javascript)

In this example, you can see the message 'DBLogger is loaded.' only once, not twice. This means that Node.js evaluated the dblogger.js only once.

Summary

  • In the CommonJS modules, Node.js treats a JavaScript file as a module.
  • Expose variables and functions to other module by assign them to the properties of the module.exports object.
  • Node.js wraps the module code in a module wrapper function before executing it.
  • All variables, constants, functions, classes, etc., declared in a module are scoped to the module, not the global scope.
  • Node.js executes a module only once and place the result in the cache for the next use.
Was this tutorial helpful ?