How to use ECMAScript modules with Node.js
Tags: es6, node
Published: 2019-07-11 21:12:24

---
title: How to use ECMAScript modules with Node.js
published: true
tags: es6,node
canonical_url: https://blog.logrocket.com/how-to-use-ecmascript-modules-with-node-js/
---

![](https://thepracticaldev.s3.amazonaws.com/i/5d7kmm87u07l1r4y50th.jpg)

Since 2009, right after Kevin Dangoor started the [CommonJS](http://www.commonjs.org/) project, a new discussion began about how JavaScript would better fit the process of building applications not only to run in web browsers, but amplifying its powers to a wider and broader range of possibilities. And those, of course, had to include the backend universe.

Its key to success is actually due to its API, which brought to the table a rich standard library similar to those we had for other languages like Python, Java, etc. Today, thanks to CommonJS, we have JavaScript in server-side applications, command line tools, desktop GUI-based and hybrid applications (Titanium, Adobe AIR, etc.), and more.

By all means, every time you use a `require()`, you’re in fact using the implementation of CommonJS ES modules — or just ESM, which comes within Node.js by default.

And that’s the first problem of using ES modules along with Node: CommonJS is already a module, and ESM had to find the best way to live with it. It shouldn’t really be a problem, except for the fact that ESM is asynchronously loaded, while CommonJS is synchronous.

When it comes to tools like [Babel](https://babeljs.io/) and [webpack](https://webpack.js.org/), the load is also taken by a synchronous process, so considering their isomorphic natures for allowing applications in both browsers and server sides to run without native support, we’ll have some issues.

In this article, we’ll explore how far this journey of supporting both worlds has come in the Node.js universe. We’ll create a couple examples to give you a closer look at how you can migrate your codebase to make use the power of ESM.

[![LogRocket Free Trial Banner](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/f760c-1gpjapknnuyhu8esa3z0jga.png?resize=1200%2C280&ssl=1)](https://logrocket.com/signup/)

## A brief introduction

If you’re a beginner in ES modules, let’s take a closer look at how to use them. If you’ve ever used React or Vue.js, you’ve probably seen something like this:
```jsx
import React, {Fragment} from 'react';
// or
import Vue from './vue.mjs';
```
The first example, in particular, is a good one because it expresses the nature of ES modules for what is a default module or not. Consider the following code snippet:
```jsx
export default React;
```
We can only have one default module exported by file. That’s why `Fragment` has to be imported into the `{ }`s once it is not included as a default. Its exportation would look like:
```jsx
export const Fragment = … ;
```
And you can, obviously, create your own, like so:
```jsx
export const itsMine = 'It is my module';
```
Go and save this code into an `mjs` extension file, and just as we saw in the React example, you can import it to another file:
```jsx
import { itsMine } from './myESTest.mjs'

alert(itsMine); // it'll alert 'It is my module' text
```
The `mjs` extension can lead to some confusion when we compare its use against `js` files. For JavaScript specification, there are differences between them. For example, modules are, by definition, strict (as in `'use strict'`), so it means that a lot of checks are made and “unsafe” actions are prohibited when implementing your JavaScript modules.

The `js` vs. `mjs` fight extends to the fact that JavaScript needs to know if it’s dealing with a module or a script, but the spec doesn’t provide it so far. If you get a CommonJS script, for example, you’re not allowed to use `'import from'` in it (just `require`), so they can force each extension to import the appropriate, respective one:

- `mjs import from mjs`
- `js require js`

So, what happens to the following scenario?

- `mjs import from js`
- `js require mjs`

When it comes to ES modules, it’s well known that they’re static — i.e, you can only “go to” them at compilation time, not runtime. That’s why we have to `import` them in the beginning of the file.

### mjs import from js

The first thing to notice here is that you cannot use `require` in a `mjs` file. Instead, we must use the import syntax we’ve previously seen:
```jsx
import itsMine from './myESTest.js'
```
But only if the default import (`module.exports`) has been exported into the CommonJS file (`myESTest.js`). Simple, isn’t it?

### js require mjs

However, when the opposite takes place, we can’t simply use:

```jsx
const itsMine require('./myESTest.mjs')
```

Remember, ESM can’t be imported via the `require` function. On the other side, if you try the `import from` syntax, we’ll get an error because CommonJS files are not allowed to use it:

```jsx
import { itsMine } from './myESTest.mjs' // will err
```

Domenic Denicola proposed [a process to dynamically import ES modules](https://github.com/tc39/proposal-dynamic-import) via the `import()` function in various ways. Please refer to the link to read a bit more about it. With it, our code will look like this:
```jsx
async function myFunc() {
const { itsMine } = await import('./myESTest.mjs')
}
myFunc()
```
Note, however, that this approach will lead us to make use of an async function. You can also implement this via callbacks, promises, and other techniques described in more detail [here](http://2ality.com/2017/01/import-operator.html).

_Note: This type of importing is only available from Node 10+._

## Running Node.js with ES modules

There are two main ways for you to run Node.js along with ES modules:

1. Via a flag `--experimental-modules`, which stores the MVP for average usage
2. Via a library, in this case [esm](https://github.com/standard-things/esm), which bundles all the main parts of the implementation in one single place, simplifying the whole process

In the Node GitHub repo, you can find a page called “[Plan for New Modules Implementation](https://github.com/nodejs/modules/blob/master/doc/plan-for-new-modules-implementation.md),” where you can follow the official plan to support ECMAScript modules in Node.js. The effort is split up into four phases, and at the time of writing, it is now in the last one, with hopes it will be mature enough to no longer require use of `--experimental-modules`.

### Using the flag –experimental-modules

Let’s start with the first (and official) way provided by Node.js ito use ES modules in your Node environment.

First, as previously mentioned, make sure to have a version of Node higher than 10 on your machine. You can use the power of [NVM](https://github.com/nvm-sh/nvm) to manage and upgrade your current version.

Then, we’re going to create a single example, just to give you a taste of how the modules work out. Create the following structure:

![Example Project Structure](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2019/07/project-structure.png?resize=500%2C122&ssl=1)<figcaption>Our project structure.</figcaption>

The first file, `hi.mjs`, will host the code for a single function that’ll concat a string param and return a hello message:
```jsx
// Code of hi.mjs
export function sayHi(name) {
    return "Hi, " + name + "!"
}
```
Note that we’re making use of the `export` feature. The second file, `runner.mjs`, will take care of importing our function and printing the message to the console:
```jsx
// Code of runner.mjs
import { sayHi } from './hi.mjs'

console.log(sayHi('LogRocket'))
```
To run our code, just issue the following command:
```jsx
node --experimental-modules runner.mjs
```

And this will be the output:

![Test Output](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2019/07/test-output.png?resize=730%2C80&ssl=1)<figcaption>Output of our test.</figcaption>

Note that Node will advise you about the ESM experimental nature of this feature.

### Using the esm library

When it comes to the use of Babel, webpack, or any other tool that would help us to use ES modules wherever we want, we have another solution for Node.js specifically that is much more succinct: it is the [@std/esm](https://github.com/standard-things/esm) package.

It basically consists of a module loader that dispenses Babel or other bundle-like tools. No dependencies are required; it allows you to use ES modules in Node.js v4+ super quickly. And, of course, it is totally compliant with the [Node ESM specification](https://github.com/nodejs/node-eps/blob/master/002-es-modules.md).

Let’s now consider a different `hello world`, this time on the web, with [Express.js](https://expressjs.com/). We’ll make a CJS file to talk with an ESM one.

But first, in the root folder of our project, run the following commands:
```jsx
npm init -y
npm install --save @std/esm
npm install --save express
```
Follow the steps, intuitively, to set up your `package.json` structure. Once finished, create two new files:

- `Runner.js` will be the starting point of execution, but now as a single JavaScript file
- `hi-web.mjs` will store the code for Express to access the hello function

Let’s start with the `hi-web.mjs` source code:
```jsx
import express from "express";
import { sayHi } from "./hi.mjs";

const app = express();

app.get("/", (req, res) => res.json({ "message": sayHi("LogRocket") }));

app.listen(8080, () => console.log("Hello ESM with @std/esm !!"));
```
Note that, here, we’re making use of the previous `mjs` file that hosts the `sayHi()` function. That’s no big news once we’ve seen that we can perfectly import `mjs` files from another one. Take a look at how we import this file to our start script:
```jsx
// runner.js code
require = require("@std/esm")(module);
module.exports = require("./hi-web.mjs").default;
```
Once we’re not using the dynamic import, the `default` must be used. The @std/esm rewrites `require` and also adds functionality to the Node version module being used. It does some inline and on-demand transformations, processing and caching to the executions in real time.

Before you run the example, make sure to adapt your `package.json` to understand which file will be the starting point:
```jsx
...

"scripts": {
    "start": "node runner.js"
},
```
After running the `npm start` command, that’ll be the output at the browser:

![Browser Output Example](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2019/07/browser-output-example.png?resize=500%2C163&ssl=1)<figcaption>Browser output.</figcaption>

## Conclusion

For more details on how ES modules work with Node, please visit their [official docs](https://nodejs.org/api/esm.html).

When dealing with codebase conversions, remember these important points:

- When migrating your `js` files to `mjs`, change the basic exports (`module.exports`) to the new ESM `export` statement
- All the `require`s must be changed to the respective import statements
- If you’re using `require` dynamically, remember to make the import as well, via `await import` (or the dynamic `import()` function we’ve seen)
- Also change the other `require`s in other files that reference what you’re migrating
- `mjs` files, when used in the browser, must be served with the correct Media Type, which is `text/javascript` or `application/javascript`. Since browsers don’t care about the extension, Node.js is the only thing that requires the extension to exist. This is the way it can detect whether a file is a CJS or an ES module

Good studies!

* * *

## Plug: [LogRocket](https://logrocket.com/signup/), a DVR for web apps

[![LogRocket Dashboard Free Trial Banner](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/1d0cd-1s_rmyo6nbrasp-xtvbaxfg.png?resize=1200%2C677&ssl=1)](https://logrocket.com/signup/)

[LogRocket](https://logrocket.com/signup/) is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

[Try it for free](https://logrocket.com/signup/).

* * *

The post [How to use ECMAScript modules with Node.js](https://blog.logrocket.com/how-to-use-ecmascript-modules-with-node-js/) appeared first on [LogRocket Blog](https://blog.logrocket.com).