DEV Community

Alessio Michelini
Alessio Michelini

Posted on • Updated on

How to use ES Modules with Node.js

As most of frontend devs have enjoyed to use ES Modules (or ECMAScript Modules) for a long time, on the backend side of things most of devs still use CommonJS, as it’s still the standard way to import modules.
Since version 8.9.0 you could start to use ES Modules by adding the —experimental-modules flag, but you should never use anything experimental in production.

But since Node version 13 you don’t really need to use that flag anymore and as long you use any version from 16 (but it's also supported from version 14.14.0 and 12.20.0), it’s now fully supported to use, you just need to do a few small things.

In this article we are going to show how to run a small express server using ES Modules.

Set the right type on your package.json

I created a small sample project in node with just express as the only dependency, just as a proof of concept, I created a folder and then initialised a new Node project with npm init -y.

Installed Express and nodemon with npm i express -S and npm i nodemon -D, and added a start script in the package.json file, ending up with something like this:

{
  "name": "node-esm",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "nodemon index.mjs"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "type": "module",
  "dependencies": {
    "express": "^4.17.1"
  },
  "devDependencies": {
    "nodemon": "^2.0.14"
  }
}
Enter fullscreen mode Exit fullscreen mode

Now, if you look closely to the code above, you might have noticed something different, the type property and the index.mjs file (we’ll discuss later about this file extension).

Regarding the first property, this has two possible values: commonjs and module.
The first one is your default value, which just tell to Node to use CommonJS , if you use the module option instead is going to tell to Node to use ECMAScript Modules instead.

And this is the only change you need to do in your package.json configuration.

Use the .mjs extension

This is the new official extension that also tells to Node that you are going to use a new ES Module, (more info here).

So in our little project we are going to have our index.mjs file, and another file that we are going to export a function to run for an endpoint, again with extension .mjs.

Our project file tree will look like this:

.
|-- modules
|   `-- test.mjs
|-- index.mjs
|-- package-lock.json
`-- package.json
Enter fullscreen mode Exit fullscreen mode

Add some code to the .mjs files

Create a simple index.mjs file with just the very basic Express implementation:

// index.mjs
import express from 'express';

const app = express();

app.use('/', (req, res) => res.status(200).send('HEALTHY'));

const { SERVER_PORT: port = 5010 } = process.env;

app.listen({ port }, () => {
  console.log(`🚀 Server ready at http://0.0.0.0:${port}`);
});
Enter fullscreen mode Exit fullscreen mode

So our modules/test.mjs will contain the following code:

// modules/test.mjs
export const sayHello = (req, res) => res.json({hello: 'world'});
Enter fullscreen mode Exit fullscreen mode

Nothing crazy here, just a function that handles an HTTP request with Express, and just return some sample JSON.

But the nice thing to see here is the export keyword!

Now let’s import this file in our index.mjs

// index.mjs
import express from 'express';
import { sayHello } from './modules/test.mjs';
Enter fullscreen mode Exit fullscreen mode

And then use it later in the code:

app.get('/hello', sayHello);
Enter fullscreen mode Exit fullscreen mode

And our index.mjs will look like this now:

import express from 'express';
import { sayHello } from './modules/test.mjs';

const app = express();

app.get('/hello', sayHello);
app.use('/', (req, res) => res.status(200).send('HEALTHY'));

const { SERVER_PORT: port = 5010 } = process.env;

app.listen({ port }, () => {
  console.log(`🚀 Server ready at http://0.0.0.0:${port}`);
});

Enter fullscreen mode Exit fullscreen mode

Start our application with npm start and here we are, our little server running with ES Modules instead of CommonJS :-)

> node-esm@1.0.0 start
> nodemon index.mjs

[nodemon] 2.0.14
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node index.mjs`
🚀 Server ready at http://0.0.0.0:5010
Enter fullscreen mode Exit fullscreen mode

This is great to see finally coming in Node.js, to standardise even more the code between frontend and backend in JavaScript!

Update 28/04/23

The .mjs file extension was a way to enable ES Modules on Node.js from version 13.x, before that you needed an experimental flag, but from Node 18.x onward that's no longer needed and you can just use .js files.

Top comments (6)

Collapse
 
jobechoi profile image
JC Choi

Says "Now let’s import this file in our index.js." Think maybe to change to index.mjs?

Collapse
 
darkmavis1980 profile image
Alessio Michelini

You are right, thanks for spotting the typo!

Collapse
 
elidvenega profile image
Elid

Thanks for this article

Collapse
 
geforcesong profile image
George Guo

if you specify type=module in package.json, you don't need mjs extension, just .js file is fine.

Collapse
 
esensats profile image
Yessen

yep. If you have "type": "module" then you only need to add .cjs/.cts for CommonJS files within the ESM environment. All of the other .js/.ts files will be interpreted as ESModules by default.
With "type": "commonjs" (which is default) it's vice versa.

Collapse
 
darkmavis1980 profile image
Alessio Michelini

When this article was written it didn't work, just switch to node 16 and try it.
From node 18 you are right, you don't need anymore the .mjs extension