I like to write small scripts, but used to suggle when I want to use Javascript to fetch API data, or use some helper packages. Then I go with Python instead. But things have changed. With the supports of ES module and fetch and node:module API. It is much easier now since I feel more comfortable using JS.

Unlike normal js files, node treats mjs files as ECMAScript modules. So it means you can use any supported ES-next features w/o a transpiler like Babel. In the past, to be able to use ES features, we had to set up build and compilation tools like Webpack and Babel (due to conflicts between common JS require is synchronous but import is asynchronous, NodeJS didn’t support import/export natively until node 13). Webpack and Babel are not ment to do the jobs of Node. anymore. We should use them now when we need code bundling and compile some other language to JS such as JSX or new ES proposals still in staging. And even worse, sometimes they can even hold you back from using new features added to Javascripts if you don’t configurate it at latest version. I have suffered so many times that I can’t use ..., or Array.at in my code just because my project configuration is so outdated, not to mention the over advertised Typescripts, migrating those tools just to use a natively supported feature is pain in the a…

For example I want to write a script to quickly check current price of BTC on Binance. Write it to a json file for later if I want to do anything with that data. And we want to do it without installing any packages at all.

First of all, we know we’ll need to write some data, so we need some helps from Node builtin fs package.

import fs from 'node:fs/promises'

Here I use node:fs/promises, new api return promises instead of callbacks. And because it is builtin package, we don’t need to install like fs.

Secondly, we need to get data from Binance API at https://api.binance.com/api/v3/ticker/price. At this point, we have Node 18 released and it added fetch, the dead simple api for fetching data has been available in browsers for a long long time. Other wise we would need to go through these tutorials again to know how to fetch data with node https://blog.logrocket.com/5-ways-to-make-http-requests-in-node-js/

So our following lines of code look like this.

const response = await fetch('https://api.binance.com/api/v3/ticker/price')
if (response.ok) {
    const data = await response.json()
}

Next, I want to find the price of BTC to USDT, so let’s loop through the data and find it.

for (const pair of data) {
    if (pair.symbol === 'BTCUSDT') {
        console.log(`Current price of BTC is ${pair.price}`)
        break
    }
}

And finally, let’s backup our data in a file for later.

await fs.writeFile('./price.json', JSON.stringify(data, null, 2))

Let’s put it all together.

import fs from 'node:fs/promises'
const response = await fetch('https://api.binance.com/api/v3/ticker/price')
if (response.ok) {
    const data = await response.json()
    for (const pair of data) {
        if (pair.symbol === 'BTCUSDT') {
            console.log(`Current price of BTC is ${pair.price}`)
            break
        }
    }
    await fs.writeFile('./price.json', JSON.stringify(data, null, 2))
} else {
    console.error('Cannot get BTC price')
    process.exit(1)    
}

If you save this file as price.js and try to run it with node price.js, you will see the following error.

price.js:1
import fs from 'node:fs/promises'
^^^^^^

SyntaxError: Cannot use import statement outside a module

Because by default, Node treats js files as Common JS module, so using import is not supported. We have three ways to let node know that we want to run our script as an ES module.

Authors can tell Node.js to use the ECMAScript modules loader via the .mjs file extension, the package.json “type” field, or the --input-type flag.

https://nodejs.org/dist/latest-v18.x/docs/api/esm.html#enabling Without chaning the file name, we can either run this command

cat price.js | node --input-type=module

or create package.json file

{
    "type": "module"
}

and then run

$ node test.js 
Current price of BTC is 21726.41000000

But I like the .mjs the most because it the fastest way.

$ node test.mjs 
Current price of BTC is 21716.93000000

In this example I also use top level await, so much easier than Python ev.run_until_complete().