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"
}
}
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
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}`);
});
So our modules/test.mjs
will contain the following code:
// modules/test.mjs
export const sayHello = (req, res) => res.json({hello: 'world'});
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';
And then use it later in the code:
app.get('/hello', sayHello);
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}`);
});
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
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)
Says "Now let’s import this file in our index.js." Think maybe to change to index.mjs?
You are right, thanks for spotting the typo!
Thanks for this article
if you specify type=module in package.json, you don't need mjs extension, just .js file is fine.
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.
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