What are Node.js ":node" protocol imports?

Gone are the days of the older require/module.exports/CommonJS import syntaxes. In recent versions of Node.js, the support for modern ECMAScript module (ESM) imports is finally on par with the browser standards, but Node.js has also added a few extra features on top of those standards.

Node.js ":node" protocol imports is a way to express that an import references a built-in package rather than a third-party npm package dependency using the syntax import {join} from 'node:path';. As the built-in Node.js API keeps growing, the risk of name conflicts with npm packages will keep growing.

When are ":node" protocol imports useful?

Any built-in import that you might have written like this:

import {createServer} from 'http';

const server = createServer((req, res) => {
  res.end();
});

server.listen(8000);

Could now be written with the ":node" protocol prefix to express and ensure that you are importing the built-in module over an npm package:

import {createServer} from 'node:http';

const server = createServer((req, res) => {
  res.end();
});

server.listen(8000);

An example of such a collision is the http package that used to be in the npm registry but got removed to prevent malicious use.

If you need to change the imports or not in your project is up to you as both ways still work, but in some cases, especially for the newer built-in APIs adding "node:" might increase the clarity and intent for other persons reading through the codebase.

More import features in the making

Since Node.js version 17.1.0, there is also experimental support for import assertions that express details about the file type you are importing. The import assertions are useful for importing JSON files:

import fooData from './foo.json' assert { type: 'json' };

Closing thoughts

During the last year, it has also been nice seeing more and more npm packages updated and embracing the ESM syntax, as this reduces the need for complex transpiler setups to run your code.

Hopefully, the JavaScript ecosystem can gradually move closer to the "batteries included" tooling setup that languages like Rust and Go can offer. Eventually, that might end the need for the complex separate Rollup/Vite/Webpack/Babel tooling still required today in most JavaScript projects.

You can find more details over at the Node.js documentation page: https://nodejs.org/api/esm.html#node-imports

Now keep your import intents nice and clear!