Use ES modules in Node without Babel/Webpack using `esm`
Tags: javascript, es6
Published: 2018-10-11 13:48:27

---
title: Use ES modules in Node without Babel/Webpack using `esm`
published: true
tags: JavaScript, es6
canonical_url: https://codewithhugo.com/use-es-modules-in-node-without-babel/webpack-using-esm/
---

Node has been implementing more and more ES6+ (ESNext) features natively. One of the features that is taking the longest to implement is modules. The reason for this is that Node and npm run on what is called CommonJS, with which you use `require(``'``module-name``'``)` to import from other modules and use the `module.exports` object to expose entities from a module.

Node’s CommonJS was actually one of the first widely adopted module systems in JavaScript. The ease with which one can bundle CommonJS coupled with its widespread use in Node applications and tools means CommonJS quickly displaced RequireJS and SystemJS for frontend application dependency and module management

CommonJS has some drawbacks, like being hard to statically analyse, which leads to for example bloated bundles. It’s also just not part of the ECMAScript specification, which ES modules are.

For anyone who is still wondering, ECMAScript (or ES) modules use a syntax with `import thing from 'my-module';` or `import { something } from 'my-module'` to import things and `export default` or `export something` to expose entities from the module.

Bundlers like Webpack, Rollup and Parcel have support for ES modules. For a Node server I’ve still tended to write in CommonJS style because Node has great support for most ESNext features out of the box (eg. rest/spread, async/await, destructuring, class, shorthand object syntax) and I don’t like messing with bundlers and transpilers.

I’ve discovered the [esm](https://github.com/standard-things/esm) module, “Tomorrow’s ECMAScript modules today!” by [John-David Dalton](https://github.com/jdalton) (of lodash 😄). It allows you to use ES modules in Node with no compilation step. It’s small, has a small footprint and comes with some extra goodies

What follows is some ways to use it that aren’t strictly documented. This covers use-cases like incremental adoption of ES modules (ie. convert some modules to ESM but not the whole app). Using this will help you share

## Import default export from an ES module in CommonJS

```js
const esmImport = require('esm')(module);
const foo = esmImport('./my-foo');
console.log(foo);

```

## Import named exports from an ES module in CommonJS

```js
const esmImport = require('esm')(module);
const { bar, baz } = esmImport('./my-foo');
console.log(bar, baz);

```

## Re-export an ES module as CommonJS

This is documented in the docs but I thought I would include it for completeness

```js
module.exports = require('esm')(module)('./my-es-module');
// see the docs
// https://github.com/standard-things/esm#getting-started

```

## Load whole application using ES modules

Again, this in the docs but including it for completeness

```
node -r esm app.js
// see the docs
// https://github.com/standard-things/esm#getting-started

```

## Using top-level await

Let’s say we have this module `cli.module.js` (taken from [github.com/HugoDF/wait-for-pg](https://github.com/HugoDF/wait-for-pg/blob/master/wait-for-pg-cli.module.js#L35-L44)):

```js
const waitForPostgres = () => Promise.resolve();

try {
  await waitForPostgres();
  console.log('Success');
  process.exit(0);
} catch (error) {
  process.exit(1);
}

```

The interesting bit is that this is using `await` without being in an `async` function. That’s something `esm` allows you to do. This can be enabled by setting `"``esm``"``: {` `"``await``"``: true }` in `package.json` but it can also be enabled at conversion time `cli.js`:

```js
const esmImport = require('esm')(module, { await: true });
module.exports = esmImport('./cli.module');

```

Lo and behold it works:

```
$ node cli.js
Success
```

That wraps up how to use ES modules now, without transpilation. There’s a more thorough walkthrough of what that means at [ES6 by example: a module/CLI](https://dev.to/hugo__df/es6-by-example-a-modulecli-to-wait-for-postgres-in-docker-compose-902).

If you’re interested in “history of JavaScript module, bundling + dependency management” article, let me know by [subscribing](https://buttondown.email/hugo).