How does the Node.js module system work?
© https://nodejs.org/en/

How does the Node.js module system work?

Understand how the Node.js module system can help to write reusable code.

ByMario Kandut

honey pot logo

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 Node.js Module System

💰 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?

What is a module?

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.

Types of Modules

Node.js has three types of modules:

  • Built-in modules
  • Local modules
  • External modules

Built-in 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.

Local modules

  • 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

  • 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.

How to use the module system?

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.

What is require?

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.

import and export (ECMAScript Modules)

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.

TL;DR

  • 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 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.
  • 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.

References (and Big thanks):

NPM modules, HeyNode, CommonJS NodeJS - ESM

More node articles:

Getting started with Webpack

How to list/debug npm packages?

How to specify a Node.js version

How to create a web server in Node.js

How to dynamically load ESM in CJS

How to convert a CJS module to an ESM

How to create a CJS module

How to stream to an HTTP response

How to handle binary data in Node.js?

How to use streams to ETL data?

How to connect streams with pipeline?

How to handle stream errors?

How to connect streams with pipe?

What Is a Node.js Stream?

Handling Errors in Node (asynchronous)

Handling Errors in Node.js (synchronous)

Introduction to errors in Node.js

Callback to promise-based functions

ETL: Load Data to Destination with Node.js

ETL: Transform Data with Node.js

ETL: Extract Data with Node.js

Event Emitters in Node.js

How to set up SSL locally with Node.js?

How to use async/await in Node.js

What is an API proxy?

How to make an API request in Node.js?

How does the Event Loop work in Node.js

How to wait for multiple Promises?

How to organize Node.js code

Understanding Promises in Node.js

How does the Node.js module system work?

Set up and test a .env file in Node

How to Use Environment Variables in Node

How to clean up node modules?

Restart a Node.js app automatically

How to update a Node dependency - NPM?

What are NPM scripts?

How to uninstall npm packages?

How to install npm packages?

How to create a package.json file?

What Is the Node.js ETL Pipeline?

What is data brokering in Node.js?

How to read and write JSON Files with Node.js?

What is package-lock.json?

How to install Node.js locally with nvm?

How to update Node.js?

How to check unused npm packages?

What is the Node.js fs module?

What is Semantic versioning?

The Basics of Package.json explained

How to patch an NPM dependency

What is NPM audit?

Beginner`s guide to NPM

Getting started with Node.js

Scroll to top ↑