Understand how the Node.js module system can help to write reusable code.
Posted
Updated
Europe’s developer-focused job platform
Let companies apply to you
Developer-focused, salary and tech stack upfront.
Just one profile, no job applications!
This article is based on Node v16.14.0.
Modules are building blocks of code structures and allow Node.js developers to better structure, reuse, and distribute code. A module is a self-contained code block in a file or in a directory, which then can be included wherever we need it. Modules and the module system are fundamental parts of how Node.js applications are written and structured.
💰 The Pragmatic Programmer: journey to mastery. 💰 One of the best books in software development, sold over 200,000 times.
The module system from Node.js helps to write reusable code, but what is a module and how can it be created or imported?
A module is a unit of code, organized into a file or folder. In a module functionality is exported so that it can be included in other parts.
In Node.js, each file has a global object in its scope called module
. This module holds information about that specific file.
The module variable is an object, which has a property called exports.
Assigning values to module.exports
will expose them for importing in other parts of the application.
Modules can be reused where needed and help to organize the applications' codebase.
Creating modules for specific tasks helps to maintain clean code.
Node.js has three types of modules:
require
.package.json
file.With modules the code can be broken up into smaller chunks and organized by functionality.
The module system lets you encapsulate a unit of code and expose it to be reused by other parts of your application.
This is done by assigning values to the file's module.exports
object.
The module system extends the CommonJS
standard.
Starting with Node.js v16 the ESM (EcmaScript Modules) are used, see docs.
In the early days of Node.js, there was no explicit module system that the language used, so CommonJS was adopted and extended to fill the gap and set forth a standard the community can use. If the NPM registry is the heart of the Node.js ecosystem, then CommonJS and the module system are the backbone.
Let's export a simple function from a module:
// math.js
const multiplyByTwo = function(x) {
return x * 2;
};
module.exports = multiplyByTwo;
In the example above, we have exported a single function from a file called math.js
, by assigning the function to module.exports
.
In any given file, we can assign a value to module.exports
, and then include that value elsewhere by passing the file's path to the require
function.
The require function loads a file or package and returns the value assigned to module.exports.
For example, we want to use the function from the above module:
// index.js
const multiplyByTwo = require('./math.js');
console.log(multiplyByTwo(10));
// 20
Multiple values can be assigned to module.exports
:
// mathFunctions.js
const add = function(x, y) {
return x + y;
};
const subtract = function(x, y) {
return x - y;
};
const multiplyByTwo = function(x) {
return x * 2;
};
module.exports = {
add,
subtract,
multiplyByTwo,
};
Another way to export values is to use the global object exports
, which is available in each file, but it is an alias of module.exports
.
// mathFunctions.js
exports.add = function(x, y) {
return x + y;
};
exports.subtract = function(x, y) {
return x - y;
};
exports.multiplyByTwo = function(x) {
return x * 2;
};
Important: Don't mix the exports
and module.exports
usages, it might result in a loss of the previous used reference.
Exporting code using module.exports
is only half of the module system.
You need a way to import the code in other parts of the application. You can do that with the require
function.
In order to load a local module, we pass its relative file path to the require function, which returns the value of module.exports
from the file.
When requiring an npm package, the name of the package is passed to the require
function, and the same thing is happening in the node_modules/
folder
A module is evaluated the first time it is passed to the require function. The code in the file will be wrapped in a private scope, run, and the value of module.exports is returned by require. After that, (the module has been required once) the module is cached, and requiring the file again will return the cached result, without evaluating the file again.
The ESM (EcmaScript Modules) uses import
and export
keywords when dealing with modules.
This is available in the current version of Node.js (v.16). If you are below this Node.js version you can use a transpiler like Babel to convert ESM import and export code into regular CommonJS format through adding a build step to the project.
When developing applications for the frontend, it's standard to use something like Babel to transpile your code to make it compatible with as many browsers as possible. So the import and export syntax can be safely used, because the code is replaced before put into production. But when developing for the backend, there is usually no need for transpilation, because the environment is controlled by the developers, not like the different browsers.
module
object or the exports
object, we can export code from a file to use elsewhere, while keeping some parts of the module encapsulated in their own private scope.Thanks for reading and if you have any questions, use the comment function or send me a message @mariokandut.
If you want to know more about Node, have a look at these Node Tutorials.
Never miss an article.