TIL – Top-level await is available in Node.js modules
Tags: javascript, node
Published: 2021-04-05 07:56:26

---
title: TIL – Top-level await is available in Node.js modules
published: true
description: Node.js' top-level is available in ES modules. Find out about three ways to enable it and untangle your code.
tags: javascript, node
canonical_url: https://www.stefanjudis.com/today-i-learned/top-level-await-is-available-in-node-js-modules/
//cover_image: https://direct_url_to_image.jpg
---

Node.js is a beautiful tool to write quick utility scripts. I use it in many of my build processes. Renaming files, downloading data, image processing – Node.js scripts handle many tasks in my projects.

There has been one tiny annoyance, though. When dealing with asynchronous functionality such as making network requests, there was no top-level await support in Node.js (yet).

`await` allows you to untangle Promises-based code and make it more readable.

```javascript
// promise-based code
Promise.resolve('hello world').then((asyncMsg) => {
  console.log(msg);
});

// async/await code
const asyncMsg = await Promise.resolve('hello world');
console.log(msg);
```

Unfortunately, you could not use the `await` keyword without wrapping it in an `async` function.

```javascript
// use an async IIFE
(async () => {
  const asyncMsg = Promise.resolve('hello world');
  console.log(asyncMsg);
})();

// use an async main function
async function main() {
  const asyncMsg = Promise.resolve('hello world');
  console.log(asyncMsg);
}

main();
```

And while this wrapping is not terrible, its whole purpose is to enable the `await` keyword. Is there a better way? Can we avoid these `async` wrappers in Node.js code? Top-level await to the rescue!

## top-level `await` is available "unflagged" in Node.js since `v14.8`

Starting with Node.js `v14.8`, [top-level await is available (without the use of the `--harmony-top-level-await` command line flag)](https://nodejs.org/en/blog/release/v14.8.0/). 

There's one catch: top-level await is only available in ES modules. There are three ways to make a Node.js script an EcmaScript module.

### Use the `mjs` file extension

Use the `.mjs` file extension and call it a day! 🎉

```javascript
// File: index.mjs
//
// Command line usage: node index.mjs

const asyncMsg = await Promise.resolve('WORKS!');
console.log(asyncMsg); // "WORKS!"
```

### Make the whole package a module

If you're developing a package you can also define the `type` property in your `package.json`.

```javascript
// File: index.js
//       (near package.json including { "type": "module" })
//
// Command line usage: node index.js

const asyncMsg = await Promise.resolve('WORKS!');
console.log(asyncMsg); // "WORKS!"
```

### Define `input-type` when evaluating string input

Sometimes you might need to pipe code into the Node.js binary or [use the `eval` flag](https://nodejs.org/api/cli.html#cli_e_eval_script). Use the `input-type` flag to specify that the passed string value is an ES module in these situations.

```
node --input-type=module \ 
  --eval="const asyncMsg = await Promise.resolve('WORKS!'); console.log(asyncMsg);"
```

## Await, await, await...

This functionality is beautiful! I'll probably stick to the `.mjs` file extension for my scripts. Renaming a script file from `js` to `mjs` is quickly done and is not introducing significant changes.

*If you like these quick tips, [I send out a weekly newsletter](https://www.stefanjudis.com/newsletter/).*