Overview of Modules in Node.js
In other languages, it’s usually better to structure our application code and break it into small pieces. However, in ode.js, we can write all our application code in one index.js file, irrespective of the complexity of our application, and the node.js interpreter will not cause issues. But, if we have very complex application, it would be better to divide our application code into small modules and then combine them into a cohesive application.
Before we deep dive into types of modules, let’s take a quick example of creating a simple module.
The above module will calculate the average of two numbers and display the output.
When we install node.js, several lightweight modules are automatically created, which provide bare minimum functionalities. These modules are compiled into the node.js binary. They are also referred to as core modules and located in the lib folder. Below are few of the built-in modules in node.js
These modules are used in our application code as reference. The code of these modules are not part of our program as they work as libraries. They are always loaded if their identifies is passed to require().
Let’s see an example:
Here require(‘url’) is what gives us access to url module. The definition of url module is not seen to us. We just need to understand when and how to use it.
Built-in functions have methods and classes which can be used when we require() the module.
Here, we use the http built in module using the require(‘http’). Once we load the http module, its classes and functions like createServer can also be used.
Node.js has a huge open source community which really boosts its ecosystem. People from around the globe contribute to the node.js community, which provides access to various modules other than the built in modules. These modules are created to solve specific problems in specific environments and then open sourced for the benefit of others. Since these modules are not part of the library, they cannot be used directly using the require unless we first install the codebase containing the module locally; once this is done, they can be integrated to our codebase.
Built in and External modules are provided by others based on their needs. However, there might be cases where you need to build your own module for your application.
Let’s create a module of our own named mydiff
We will put this code in a separate file called mydiff.js which provides helper function that returns a difference of two numbers. This file can provide attributes to the outer world via exports, then index.js can use the exported functionality.
Below is the content of index.js
require () will again come into play by making the attributes of local mydiff module available. Module can also allow hiding to hide the functionality of the module which is not needed outside of the module. This can be done simply by not exporting the functionality via exports. In such case even though we will have mydiff.js file specified in index.js it will not be able to access the non-exported function and fail with the following error.
This way, we can keep our codebase more organized by hiding the implementation details in a module and exposing only those parts which will be used by other parts of the codebase.
When the modules are first loaded, they are cached. This means every call to the require () will get the same object returned for multiple calls if the call is resolved to the same file. This is an important feature as it allows us to load the transitive dependencies for returning partial objects. Module caching is done in an object in a key-value pair and then we can reference the objects using the cache (require.cache). If we delete any key from this object, then we need to reload the module on next require () call.
Caching Modules also have some caveats as the modules are cached based on their filenames. If a module is resolved to a different filename based on the location of calling module it is not guaranteed that require () will always return the same object.
Also, in case of different case-insensitive file systems, different resolved file systems points to the same file. In such a case, again, cache will treat them as separate which will result in reloading the module multiple times. For example, require (‘. /mydiff’) and require (‘. / Mydiff’) would result in two different objects.
If a node.js module exposes an instance of some stateful object like db connection or socket etc., it is stateful module. Whereas, if a module exposes stateless entities like classes, methods etc., then it is known as stateless module.
If an application codebase uses stateful module, we should follow the singleton pattern so that all other modules requiring stateful module access the same instance.
Due to the concept of CommonJs system we don’t have to explicitly wrap our stateful module in Singleton pattern. The first time we use the module, it will be cached for the subsequent require as while caching, node.js uses instance package address as a key.
Stateful modules should be used with more caution than stateless modules if used in the tightly coupled architecture.
Any component or piece of information that is needed for the working of the module is regarded as dependency, but the first thing that comes to mind while thinking about the dependencies of node.js is the content of the node_modules folder. From database connection instance to string with file path can be considered as dependency. In a broader classification, dependencies are divided as ‘hardcoded dependency’ and ‘injected dependency’.
If you hardcode the name of the dependency inside the module using a require function in the wiring pattern, it is referred to as ‘hardcoded dependency’. If the name of dependency is instead provided as input by external entity, then it is known as ‘dependency injection’.
Let’s see some differences between hardcoded dependency and dependency injection.
To understand how node.js modules work, we need to know the various functionalities it offers. With this objective in mind, we first looked at the different types of node.js modules. We then explored built in modules, looking at different types of modules and examined what they offer and the advantages of having these modules.
We then saw External modules and walked through how they can be included in our codebase, including how the module code could be hidden if we so wished. Finally, we saw custom modules, their benefits and how to use them effectively. We examined how modules can be cached in modules providing us with performance gains by avoiding reload several times, while also going through some caveats we should be aware of.
We also saw how modules can be stateful and stateless and where both can fit in. Finally, we explored the module dependencies and few differences between them which helps us choose among them as per the need of the application.
Research & References of Overview of Modules in Node.js|A&C Accounting And Tax Services