مدونة

Share this post :

Node.js Design Patterns: The Module System

“Write code that is easy to delete, not easy to extend” - Tef, Programming is Terrible

At the time of writing of these lines, Node.js comes with two module systems: CommonJS and ECMAScript modules, in this article we will talk about CommonJS and specifically the revealing module pattern.

By the end of this article, you will have an outstanding understanding of what happens behind the scene when we try to load a module using require function.

Modules are the building blocks of structuring applications and the main technique to apply information hiding by making all the functions and variables that are not marked to be exported private.

A common pattern that is used to perform information hiding is the revealing module pattern. Next, we will use this pattern to build a simple module system.

The revealing module pattern

One of JavaScript in the browser problems is the lack of namespacing, scripts that run in global scope; therefore, internal application code or third-party dependencies can pollute the scope while exposing their own piece of functionality. This can have negative side effects, let’s take the example of a third-party library that instantiates a global variable called helper . If any other library or the application code overrides or changes helper , the code that depends on it will crash! The same problem will occur again if any other libraries or the application invokes a function of another library meant for internal use only.

In essence, relying on the global scope is insecure especially if your application grows and you have to rely more on third-party libraries’ functionality.

A popular approach to solve this kind of problems is called the revealing module pattern, and we can demonstrate it this way:

const myModule = (() => {
   const privateFunc = () => {}
   const privateArray = []
   const exported = {
      publicFuncOne: () => {},
      publicFuncTwo: () => {}
   }
   return exported;
})()

// output
console.log(myModule)
console.log(myModule.privateFunc, myModule.privateArray)

Once the parenthesis in line 9 are parsed, the function will be invoked. This type of function is called a self-invoking function, it is also called Immediately Invoked Function Expression and its use here is to create a private scope by exporting only the parts that are meant to be public.

Variables that are created inside a function are not accessible from the outside, the return statement is used to select what are the values that should be propagated to the outer scope. This pattern is taking advantage of these properties to keep private information hidden and export only a public-facing API.

The output of our code will be like this:

{ publicFuncOne: [Function: publicFuncOne],
  publicFuncTwo: [Function: publicFuncTwo] }
undefined undefined

The output shows only the exported API which is in myModule variable, the rest of the content is hidden which means that it can not be accessible from the outside.

The idea behind this pattern is used as a foundation for the CommonJS module system, and we will see that next.

CommonJS modules:

CommonJS is the first module system originally built into Node.js, the two of the main concepts of CommonJS are:

● require is a function that gives you the ability to import a module from the local filesystem.

● exports and module.exports: special variables used to export public functionality from the current module.

This information is enough, for now, to create our first module loader in order to understand more how CommonJS works in Node.js.

Let’s create our first module loader:

We are all familiar with this kind of code when writing Node.js applications:

const module = require(‘moduleName’);

And if asked, we all know that we are importing this module from the filesystem to take advantage of its functionalities.

We will create a function that imitates the functionality of the original require function in Node.js, and after explaining the code you will be able to envisage how the require function works behind the scene.

function require(moduleName) {
    const id = require.resolve(moduleName);        
    if(require.cache[id]) {   					
   	 return require.cache[id].exports
    }
    // module metadata
    const module = { 							 
   	 exports: {},
   	 id
    }
    // Update the cache    
    require.cache[id] = module;
   				 
    // load module
    loadModule(id, module, require); 
  		 
    // return exported variables
    return module.exports;   					 
}

1- A module name is accepted as input, it will be used to resolve the path of the module.

2- If the module we are asking for has been loaded in the past, then it should be available in the cache so we should return it immediately.

3- If the module has never been loaded before we start setting up our environment for module load, we create a module object that contains an exports property that is equal to an empty object. This

object will be populated by the code of the module to export its public API.

4- We cache the module after the first load.

5- The module source code will be loaded from its file, we give the module our module object which has been created recently, and a reference to the require function. The module will be able to export its public API by replacing or modifying the module.exports object.

6- The content of module.exports will be returned to the caller.

Everything inside a module is private except when it is assigned to the module.exports variable. The content of module.exports is cached and returned when the module is loaded using require() function.

These are all the steps performed when you ask for a module in your code using the require function.

Things to know about the require function:

● The require function is synchronous, it returns the module content with a direct style and no callbacks are required.

● The synchronous nature of require() has consequences on module definition, we will be mostly using synchronous code.

● Trying asynchronous initialization steps in module definition will lead us to a problem, which is the uncertainty of the module’s availability when we try to load it with require(), But there are advanced approaches to solve this problem, and maybe we will see it in the coming articles.

● In its early days, Node.js used to have an asynchronous version of require() but it was soon removed.

Summary:

In this article we went through:

1. The revealing module pattern design pattern.

2. CommonJS modules and its relationship with the revealing module pattern.

3. We learned fundamental details about require function behavior and use.

The article is based on Mario Casciaro & Luciano Mammino’s exceptional Book: Node.js Design Patterns, In case you want to expand your knowledge about this topic.

Share this post :

Sign up for Newsletters