20
How does the Node.js module system work?
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 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:
- Built-in modules
- Local modules
- External modules
- Built-in modules are distributed with Node.js itself. No separate installation required.
- You load them using
require
. - These built-in modules make up the standard library of Node.js.
- The built-in modules of Node.js are developed by the core Node.js team, and are part of the language itself.
- These are modules you write yourself, they are part of your actual codebase and are checked into version control.
- Local modules are how to reuse code in your project.
- An example would be creating a file for utilities. You can then export code from that file to be used in different parts of your application.
- External modules are basically NPM packages.
- An external module is installed as a dependency, tracked in your package.json, and located in the node_modules/ directory.
- The actual code of an external module is not checked into version control, since the reference is tracked with the
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.
- The module system helps us reuse code throughout our application.
- A module is a self-contained file or directory of related code.
- There are three main types of modules: built-in, local and external
- Using the global
module
object or theexports
object, we can export code from a file to use elsewhere, while keeping some parts of the module encapsulated in their own private scope. - Project-agnostic modules can be published to the NPM registry as packages for reuse across projects.
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.
20