[SOLVED] NODE err_unsupported_dir_import

Understanding and fixing NODE err_unsupported_dir_import issues

Mar 5, 2023 | Read time 7 minutes

🔔 Table of contents

Introduction

Recently I was working on a Node backend API and came across this error:

Error [ERR_UNSUPPORTED_DIR_IMPORT]: 
Directory import '/Users/<username>/Desktop/Projects/node-starter/src/db' is not supported resolving ES modules imported from /Users/<username>/Desktop/Projects/node-starter/src/app.js

This error happens in NodeJS applications when you are attempting to use the import statement to import a directory in Nodejs instead of referencing each file directly.

In Node.js, the import statement is used to load modules, and it can only be used to import individual files, not entire directories. If you try to import a directory with import, you’ll get the err_unsupported_dir_import error.

In my instance, I wanted to import a dbHelper module in my project. Consider the following project structure:

└── My project/
    ├── node_modules
    ├── app.js
    ├── db/
    │   └── index.js
    └── package.json

In my app.js file, I wanted to use the modules in the db folder:

import dbHelper from './db';

dbHelper();

Now, in my db folder, I have a index.js file like this:

import mongoose from 'mongoose';

export default connect = async () => {
    try {
        await mongoose.connect('...', { });
    } catch (error) {}
};

When running the app, it gives me the error ERR_UNSUPPORTED_DIR_IMPORT

There are a few ways we can approach to fix this error of ERR_UNSUPPORTED_DIR_IMPORT:

  1. Updating our file’s import syntax
  2. Using the –experimental-specifier-resolution=node flag
  3. Configure Babel to import directory import
  4. Use dynamic imports to import the directory

1. How to fix err_unsupported_dir_import?

We can fix this error by changing the import statement to specifically reference the JS file with the module we want to use.

Node does not support directory imports!

So instead of the following code:

import myModule from './myModule';

We change it to:

import myModuleFile1 from './myModule/file1.js';
import myModuleFile2 from './myModule/file2.js';

Note: Mandatory file extensions

When importing a module, we need to specify the file extension. A file extension must be provided when using the import keyword to resolve relative or absolute specifiers.

2. Using the –experimental-specifier-resolution=node flag

Now if you really want to use directory imports, then there is a node flag of --experimental-specifier-resolution=node.

Update - not available in Node versions 19+

The --experimental-specifier-resolution=node was first available in Node.js v12, and the last version that it was still available was in v18. For Node version 19+ it no longer exists so you need to use alternative options!

If you run your script with this flag on, then node will import directories:

node --experimental-specifier-resolution=node app.js

If you are starting your application with npm start, then just update the package.json file like so:

{
  "scripts": {
    "start": "node --experimental-specifier-resolution=node app.js"
  }
}

3. Configure Babel to import directory import

One option is to use Babel to convert our code to use the latest JavaScript features - such as directory imports.

What is Babel?

Babel is a JavaScript transpiler. This means that it converts one version of JavaScript to another version - a sort of mix between the words “translate” and “compiler”

In our case we can use it to convert modern JavaScript code written in newer syntax, such as ECMAScript 6 (ES6) and beyond, into code that can run on older web browsers and other environments that don’t support the latest JavaScript features.

We can install the following NPM packages:

  1. Open up the terminal and go to your root directory
  2. Run the following npm installs:

npm install --save-dev @babel/cli @babel/core @babel/node @babel/preset-env

  1. Update your babel.config.json file to have the following preset:
{
    "presets": ["@babel/preset-env"]
}

Now you should be able to use the directory imports without getting the error of err_unsupported_dir_import

4. Use dynamic imports to import the directory

Another option if the previous solutions did not mean your needs is to use dynamic imports!

Dynamic imports is a recent supported function of Node 10 (2018) and allows you to load modules dynamically during runtime instead of statically at compile time!

Dynamic imports can be useful in a number of scenarios, such as lazy-loading modules only when they’re needed, conditionally loading modules based on user input, etc

So to achieve directory imports, our reasoning is as follows:

  1. Import the the fs library
  2. Use the fs library to loop through all files in a directory and import the modules.

As an example, the below imports all modules under the database folder:

import fs = require('fs');

fs.readdir('./database', (err, files) => {
 files.forEach(file => {
  const module = import('./' + file).then(m =>
    m.callSomeMethod();
  );

  });
});

We can improve this with using awaits

import fs = require('fs');

fs.readdir('./database', (err, files) => {
 files.forEach(file => {
  const module = await import('./' + file);

  // do something with the module
  });
});

The problem that I don’t like with the code above dynamic code is that it complicates the matter.

There could be problems down the line when you change the modules (eg methods, properties) and it can be cumbersome to debug since we are importing modules dynamically at runtime.

My prefered option is **option 1 **where we list out all modules that we are going to use in the code! This makes the code easier to read and not a pain to debug. We know exactly the modules we are working with and and quickly go to the offending file!

Summary

In this post, I went over the issue of NODE err_unsupported_dir_import. This error is caused by our code using directory imports which are not supported in Node.

To fix this issue we need to specifically list out the module (eg with the file name and extension) that our code will use.

Another approach is to use the flag of experimental-specifier-resolution=node. However be careful when you are using this flag since it was introduced in Node version 12 and available up to Node version 18. Node version 19+ have removed this flag!

Alternatively, if directory imports is a must for your project, then you can use Bable to transpile your JavaScript code or use the dynamic import method.

👋 About the Author

G'day! I am Huy a software engineer based in Australia. I have been creating design-centered software for the last 10 years both professionally and as a passion.

My aim to share what I have learnt with you! (and to help me remember 😅)

Follow along on Twitter , GitHub and YouTube