Getting started with Node.js modules: require
,
exports
, imports
, and beyond.
Modules are a crucial concept to understand Node.js projects. In
this post, we cover Node modules: require
,
exports
and, the future import
.
Node modules allow you to write reusable code. You can nest them one inside another. Using the Node Package Manager (NPM), you can publish your modules and make them available to the community. Also, NPM enables you to reuse modules created by other developers.
We are using Node 12.x for the examples and ES6+ syntax. However, the concepts are valid for any version.
In this section, we are going to cover how to create Node modules and each one of its components:
- Require
- Exports
- Module (module.exports vs. export)
- Import
Require
require
are used to consume modules. It allows you
to include modules in your programs. You can add built-in core
Node.js modules, community-based modules
(node_modules
), and local modules.
Let’s say we want to read a file from the filesystem. Node has a core module called ‘fs’:
1 |
const fs = require('fs'); |
As you can see, we imported the “fs” module into our code. It allows us to use any function attached to it, like “readFile” and many others.
The require
function will look for files in the
following order:
-
Built-in core Node.js modules (like
fs
) -
NPM Modules. It will look in the
node_modules
folder. -
Local Modules. If the module name has a
./
,/
or../
, it will look for the directory/file in the given path. It matches the file extensions:*.js
,*.json
,*.mjs
,*.cjs
,*.wasm
and*.node
.
Let’s now explain each in little more details with
Built-in Modules
When you install node, it comes with many built-in modules. Node comes with batteries included ;)
Some of the most used core modules are:
- fs: Allows you to manipulate (create/read/write) files and directories.
- path: utilities to work with files and directories paths.
- http: create HTTP servers and clients for web development.
- url: utilities for parsing URLs and extracting elements from it.
These you don’t have to install it, you can import them and use them in your programs.
NPM Modules
NPM modules are 3rd-party modules that you can use after you install them. To name a few:
- lodash: a collection of utility functions for manipulating arrays, objects, and strings.
-
request: HTTP client simpler to use than the built-in
http
module. -
express: HTTP server for building websites and API. Again, simpler
to use than the built-in
http
module.
These you have to install them first, like this:
1 |
npm install express |
and then you can reference them like built-in modules, but this
time they are going to be served from the
node_modules
folder that contains all the 3rd-party
libraries.
1 |
const express = require('express'); |
Creating your own Nodejs modules
If you can’t find a built-in or 3rd-party library that does what
you want, you will have to develop it yourself. In the following
sections, you are going to learn how to do that using
exports
.
Exports
The exports
keyword gives you the chance to
“export” your objects and methods. Let’s do an example:
1 |
const PI = 3.14159265359; |
In the code below, we are exporting the area
and
circumference
functions. We defined the
PI
constant, but this is only accessible within the
module. Only the elements associated with
exports
are available outside the module.
So, we can consume it using require
in another file
like follows:
1 |
const circle = require('./circle'); |
Noticed that this time we prefix the module name with
./
. That indicates that the module is a local file.
Module Wrapper
You can think of each Node.js module as a self-contained function like the following one:
1 |
(function (exports, require, module, __filename, __dirname) { |
We have already covered exports
and
require
. Notice the relationship between
module.exports
and exports
. They point
to the same reference. But, if you assign something directly to
exports
you will break its link to
module.exports
— more on that in the next section.
For our convenience __filename
and
__dirname
are defined. They provide the full path
to the current file and directory. The latter excludes the
filename and prints out the directory path.
For instance, for our ./circle.js
module, it would
be something like this:
-
__filename
:/User/adrian/code/circle.js
-
__dirname
:/User/adrian/code
Ok, we have covered exports
, require
,
__filename
, and __dirname
. The only
one we haven’t covered is module
. Let’s go for it!
Module.exports vs. Exports
The module
is not global; it is local for each
module. It contains metadata about a module like id, exports,
parent, children, and so on.
exports
is an alias of module.exports
.
Consequently, whatever you assign to exports
is
also available on module.exports
. However, if you
assign something directly to exports, then you lose the shortcut
to module.exports
. E.g.
1 |
class Cat { |
Try the following case with exports
and then with
module.exports
.
1 |
const Cat = require('./cat'); |
To sum up, when to use module.exports
vs
exports
:
Use exports
to:
-
Export named function. e.g.
exports.area
,exports.circumference
.
Use module.exports
to:
-
If you want to export an object, class, function at the root level (e.g.
module.exports = Cat
) -
If you prefer to return a single object that exposes multiple assignments. e.g.
module.exports = {area, circumference};
Imports
Starting with version 8.5.0+, Node.js supports ES modules
natively with a feature flag and new file extension
*.mjs
.
For instance, our previous circle.js
can be
rewritten as circle.mjs
as follows:
1 |
const PI = 3.14159265359; |
Then, we can use import:
1 |
import { area, circumference } from './circle.mjs'; |
And, finally you can run it using the experimental module feature flag:
1 |
node --experimental-modules main.mjs |
If you don’t like experimental modules, another alternative is to use a transpiler. That converts modern JavaScript to older versions for you. Good options are TypeScript, Babel, and Rollup.
Troubleshooting import
and
require
issues
Experimental Flag
If you don’t use the experimental flag
node --experimental-modules
and you try to use
import
you will get an error like this:
1 |
internal/modules/cjs/loader.js:819 |
File extension .mjs vs .js (or .cjs)
If you have a *.mjs
file you cannot use
require
or it will throw an error (ReferenceError: require is not defined
). .mjs
is for import
ECMAScript
Modules and .js
is for regular
require
modules.
However, with *.mjs
you can load both kinds of
modules!
1 |
import { area, circumference } from './circle.mjs'; |
Notice that cat.js
is using commonJS modules.
Summary
We learned about how to create Node.js modules and used it in
our code. Modules allow us to reuse code easily. They provide
functionality that is isolated from other modules. The
require
function is used to load modules. The
exports
and module.exports
allow us to
define what parts of our code we want to expose. We also
explored the difference between module.exports
and
exports
. Finally, we took a quick pick about what’s
coming up for modules using imports
.