NodeJS modules

require() vs import

In the REPL all node modules are already present so you do not have to explicitly require() them first. But when you write your own files you must choose between one of two options:

// file.js
const http = require('http');

or use ES6-style with .mjs file extension.

// file.mjs
import http from 'http';

How modules are resolved

There are a number of steps involved whenever you require('something'):

  1. Resolving
  2. Loading
  3. Wrapping
  4. Evaluating
  5. Caching

The difference between require('something') and require('./something') is that the first will search for the module in a folder node_modules starting from the file location and – if not found – goes all the way up to the root. Whereas the second invocation will look up for a file in the current folder.

require('something') looks for files in this order: something.js, something.json and something.node. You can check their corresponding functions with require.extensions.

If you require() a module the underlying file gets loaded. You can use require.resolve() without loading the module, which you might want to do to check if an optional package is installed or not.

If a folder is specified as a module (instead of a file) the package.json‘s "main" property of this folder/package is looked up and the associated file is loaded (or index.js if "main" is not specified).

Modules are wrapped in a hidden function

Node wraps your code in a (hidden) function, that means that you can access this function’s arguments:

// function(exports, module, require, __filename, __dirname) {
console.log(arguments);
module.exports.a = 42;
// }

Now you know that require or exports for example are not a global variables, but instead are passed in by a hidden function. Also any variable defined inside that function is not global.

The hidden function also returns module.exports by default for every file. So when you call

// a.js
const bVar = require('b');

then bVar actually points to module.exports from file b.js.

Be aware that there is a difference between

exports = { hello: 'there' } // will not export the object
// and
module.exports = { hello: 'there' } // will work

In the first case we are simply reassigning a variable to an object. In the second case we are changing the reference to a new object, what is what we usually want.

Checking whether module was required() or started from CLI

To test if a module is run as script from command line or via a required() statement:

const someFunction = () => {
  console.log("I do something");
}

if(require.main === module) {
  // run as script from command line. Read in command line arguments
  print(process.argv[2], process.argv[3]);
} else {
  // being required
  module.exports = someFunction;
}

Now myFile.js can be required or run as a script.

Modules are cached

Modules are cached, which means if you require them twice or more, then they will only get required once. You can verify that by checking require.cache which holds an array of all cached modules. One way to prevent caching might be to just remove the file entry from that array. But a better way is to simply export the functionality of the module as a function:

module.exports = () => {
  console.log("I was called");
}

Now you can call it twice and it will also console.log twice:

require('myFile.js')();
require('myFile.js')();

Debug modules by setting an env var

Setting NODE_DEBUG="http" will output debug info to the console for Node’s http module. Use a comma seperated list to debug more modules at the same time. You can adjust NODE_PATH to tell Node where to look for modules.

About Author

Mathias Bothe To my job profile

I am Mathias from Heidelberg, Germany. I am a passionate IT freelancer with 15+ years experience in programming, especially in developing web based applications for companies that range from small startups to the big players out there. I create Bosycom and initiated several software projects.