Understanding Modules in Node.js. Why are there so many types of modules?

Understanding Modules in Node.js. Why are there so many types of modules?

Daily short news for you
  • For over a week now, I haven't posted anything, not because I have nothing to write about, but because I'm looking for ways to distribute more valuable content in this rapidly exploding AI era.

    As I shared earlier this year, the number of visitors to my blog is gradually declining. When I looked at the statistics, the number of users in the first six months of 2025 has dropped by 30% compared to the same period last year, and by 15% compared to the last six months of 2024. This indicates a reality that users are gradually leaving. What is the reason for this?

    I think the biggest reason is that user habits have changed. They primarily discover the blog through search engines, with Google being the largest. Almost half of the users return to the blog without going through the search step. This is a positive signal, but it's still not enough to increase the number of new users. Not to mention that now, Google has launched the AI Search Labs feature, which means AI displays summarized content when users search, further reducing the likelihood of users accessing the website. Interestingly, when Search Labs was introduced, English articles have taken over the rankings for the most accessed content.

    My articles are usually very long, sometimes reaching up to 2000 words. Writing such an article takes a lot of time. It's normal for many articles to go unread. I know and accept this because not everyone encounters the issues being discussed. For me, writing is a way to cultivate patience and thoughtfulness. Being able to help someone through my writing is a wonderful thing.

    Therefore, I am thinking of focusing on shorter and medium-length content to be able to write more. Long content will only be used when I want to write in detail or delve deeply into a particular topic. So, I am looking for ways to redesign the blog. Everyone, please stay tuned! 😄

    » Read more
  • CloudFlare has introduced the pay per crawl feature to charge for each time AI "crawls" data from your website. What does that mean 🤔?

    The purpose of SEO is to help search engines see the website. When users search for relevant content, your website appears in the search results. This is almost a win-win situation where Google helps more people discover your site, and in return, Google gets more users.

    Now, the game with AI Agents is different. AI Agents have to actively seek out information sources and conveniently "crawl" your data, then mix it up or do something with it that we can't even know. So this is almost a game that benefits only one side 🤔!?

    CloudFlare's move is to make AI Agents pay for each time they retrieve data from your website. If they don’t pay, then I won’t let them read my data. Something like that. Let’s wait a bit longer and see 🤓.

    » Read more
  • Continuing to update on the lawsuit between the Deno group and Oracle over the name JavaScript: It seems that Deno is at a disadvantage as the court has dismissed the Deno group's complaint. However, in August, they (Oracle) must be held accountable for each reason, acknowledging or denying the allegations presented by the Deno group in the lawsuit.

    JavaScript™ Trademark Update

    » Read more

Issues

When building Vue.js or React.js applications, you often use the import syntax to use a component or module. However, with plain JS Node.js applications, you typically use require:

const fs = require('fs'); // CommonJS
// or
import { Component } from 'React'; // ES6 module

Or sometimes you may come across the case where modules are declared using AMD (Asynchronous Module Definition) syntax as shown below:

define(['jquery'] , function ($) {
    return function () {};
});

From these examples, have you ever wondered why sometimes you can use import, sometimes use require, or even why you can't use import syntax normally when starting a Node.js application?

// file index.js
import * as fs from "fs";
fs.mkdir('/temp/temp', () => console.log("success"));

Running:

$ node index.js
(node:2912) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.  
/Users/hoaitx/index.js:1
import * as fs from "fs";
^^^^^^

SyntaxError: Cannot use import statement outside a module
    at wrapSafe (internal/modules/cjs/loader.js:931:16)
    at Module._compile (internal/modules/cjs/loader.js:979:27)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1035:10)
    at Module.load (internal/modules/cjs/loader.js:879:32)
    at Function.Module._load (internal/modules/cjs/loader.js:724:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:60:12)
    at internal/main/run_main_module.js:17:47

Then an error will be thrown as shown above! So what is the difference between them and why are there these differences? This article hopes to help answer those questions.

1. CommonJS and AMD

For those who don't know, require is the syntax of CommonJS, which is used to create a module system for Node.js from the early days. The goal of CommonJS is to provide a module system for Node.js applications. Since JavaScript did not have an official module system at that time, CommonJS was designed to fill that gap. One characteristic of CommonJS is that it is designed for "loading" modules synchronously, and this is not suitable for browser environments, as synchronously loading modules will significantly decrease page loading performance.

On the contrary, AMD is designed to be more suitable for browser environments. RequireJS is a famous implementation of AMD. The new feature in AMD is the define() function, which allows declaring a module's dependencies before it is loaded.

define('module/id/string', ['module', 'dependency', 'array'], 
function(module, factory function) {
    return ModuleContents;  
});

The module loading in AMD can be done asynchronously, which improves page loading performance compared to synchronous loading.

AMD modules

In summary, CommonJS and AMD are different JavaScript module specifications with different implementations. CommonJS is used more frequently in server-side applications (Node.js) while AMD is used more frequently in client-side applications (browsers).

AMD was initially a draft specification for the module format of CommonJS, but it did not achieve consensus among the development community, so it was transferred to the amdjs group. The arguments mainly revolved around finding a better format, and in the end, CommonJS was created for server-side application development due to its synchronous nature, while AMD was more suitable for client-side development (browsers) with its asynchronous nature. *Reference*.

2. ES6 Module (ESM)

And since ES6 was introduced, it brought the official module system for JavaScript. So is everything about modules now resolved? The syntax of ESM mainly revolves around the import and export keywords:

import * as fs from "fs"; // import
export * from "fs"; // export

ECMAScript (ES) is just a specification, and the adoption of these specifications depends on software developers (web browsers, Node.js, etc.), or even older applications that have stopped receiving updates (IE browsers) and have no ability to handle the latest ES features. That's why nowadays we have many tools that support running JS code in different Node.js versions, older browsers. This helps us write code in the latest syntax but behind the scenes, it is converted to older ES versions to be compatible with most browsers and older Node.js versions.

Some tools like Babel, Webpack integrate tools that help us transpile code from higher versions of ES down to lower ES versions that most browsers can understand.

To keep up with the trend, Node.js has added support for ESM starting from version 12 LTS. However, up to the present (v14 LTS), ESM is still an experimental feature. There are several ways to "legitimize" the use of ESM in Node.js versions 12 and above, including:

  • Naming the file with the .mjs extension
  • Adding a "type" attribute with a value of "module" in the package.json file
  • Or using the --input-type flag in the command.
// file index.mjs
import * as fs from "fs";
fs.mkdir('/temp/temp', () => console.log("success"));
$ node index.mjs
(node:3933) ExperimentalWarning: The ESM module loader is experimental.  
success

3. Conclusion

CommonJS and AMD are module specifications that were established before ESM was introduced in ES6. ESM is the "real deal," but the popularity of CommonJS has made it the default module system of Node.js.

Nowadays, with the development of tools that support transpiling code from higher ES versions to lower ES versions, we can write JavaScript code using new syntax but behind the scenes, it is transpiled to older versions to be compatible with most browsers and older Node.js versions.

Premium
Hello

The secret stack of Blog

As a developer, are you curious about the technology secrets or the technical debts of this blog? All secrets will be revealed in the article below. What are you waiting for, click now!

As a developer, are you curious about the technology secrets or the technical debts of this blog? All secrets will be revealed in the article below. What are you waiting for, click now!

View all

Subscribe to receive new article notifications

or
* The summary newsletter is sent every 1-2 weeks, cancel anytime.

Comments (0)

Leave a comment...