NX Error [ERR_REQUIRE_ESM] - express application - unable to import ESM libs
Asked Answered
T

0

7

I've recently created an nx workspace and added in an Express application. This works out-of-the-box as you would expect. However, as soon as I add a dependency to a ESM library (e.g. nanoid v4+), I'm unable to start the server and I get the following error message.

Error [ERR_REQUIRE_ESM]: require() of ES Module /workspace/node_modules/nanoid/index.js not supported

What is the correct way to configure an NX workspace and Express application so it is able to import both ESM libraries and local workspace libraries and successfully test/build/serve the application?

  • npx nx run my-express-api:test
  • npx nx run my-express-api:build
  • npx nx run my-express-api:serve

Attempt 1

This is the out-of-the-box attempt

Error:

Error [ERR_REQUIRE_ESM]: require() of ES Module /workspace/node_modules/nanoid/index.js not supported.

Instead change the require of index.js in null to a dynamic import() which is available in all CommonJS modules.
    at Module._load (/workspace/node_modules/@nx/js/src/executors/node/node-with-require-overrides.js:18:31)
    at Array.__webpack_modules__ (/workspace/dist/apps/my-express-api/main.js:26:18)
    at __webpack_require__ (/workspace/dist/apps/my-express-api/main.js:49:41)
    at /workspace/dist/apps/my-express-api/main.js:69:18
    at /workspace/dist/apps/my-express-api/main.js:81:3
    at Object.<anonymous> (/workspace/dist/apps/my-express-api/main.js:83:12)
    at Module._load (/workspace/node_modules/@nx/js/src/executors/node/node-with-require-overrides.js:10:31)

Config:

express app tsconfig.app.json

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "outDir": "../../dist/out-tsc",
    "module": "commonjs",
    "types": ["node", "express"]
  },
  "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"],
  "include": ["src/**/*.ts"]
}

express app webpack.config.js

const { NxWebpackPlugin } = require('@nx/webpack');
const { join } = require('path');

module.exports = {
  output: {
    path: join(__dirname, '../../dist/apps/my-express-api'),
  },
  plugins: [
    new NxWebpackPlugin({
      target: 'node',
      compiler: 'tsc',
      main: './src/main.ts',
      tsConfig: './tsconfig.app.json',
      assets: ['./src/assets'],
      optimization: false,
      outputHashing: 'none',
    }),
  ],
};

express app project.json

{
  "name": "my-express-api",
  "$schema": "../../node_modules/nx/schemas/project-schema.json",
  "sourceRoot": "apps/my-express-api/src",
  "projectType": "application",
  "targets": {
    "serve": {
      "executor": "@nx/js:node",
      "defaultConfiguration": "development",
      "options": {
        "buildTarget": "my-express-api:build"
      },
      "configurations": {
        "development": {
          "buildTarget": "my-express-api:build:development"
        },
        "production": {
          "buildTarget": "my-express-api:build:production"
        }
      }
    }
  },
  "tags": []
}

root workspace package.json

{
  "name": "nx-template",
  "version": "0.0.0",
  "license": "MIT",
  "scripts": {
    "start": "nx serve",
    "build": "nx build",
    "test": "nx test"
  },
  "private": true,
  "dependencies": {
    "@emotion/react": "11.11.1",
    "@emotion/styled": "11.11.0",
    "axios": "^1.6.0",
    "express": "^4.18.1",
    "lodash-es": "^4.17.21",
    "nanoid": "^5.0.5",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "tslib": "^2.3.0"
  },
  "devDependencies": {
    "@emotion/babel-plugin": "11.11.0",
    "@nx/eslint": "18.0.4",
    "@nx/eslint-plugin": "18.0.4",
    "@nx/express": "18.0.4",
    ...
  }
}

Attempt 2

Modifying tsconfig/webpack settings.

These modifications aren't ideal but this does allow me to import ESM libraries. However, I now can't import local workspace libraries.

Error:

ERROR in ./apps/my-express-api/src/main.ts 9:0-35
Module not found: Error: Can't resolve 'test-lib' in '/workspace/apps/my-express-api/src'
resolve 'test-lib' in '/workspace/apps/my-express-api/src'
  Parsed request is a module
  using description file: /workspace/apps/my-express-api/package.json (relative path: ./src)
    resolve as module
      /workspace/apps/my-express-api/src/node_modules doesn't exist or is not a directory
      /workspace/apps/my-express-api/node_modules doesn't exist or is not a directory
      /workspace/apps/node_modules doesn't exist or is not a directory
      looking for modules in /workspace/node_modules
        single file module
          using description file: /workspace/package.json (relative path: ./node_modules/test-lib)
            no extension
              /workspace/node_modules/test-lib doesn't exist
            .js
              /workspace/node_modules/test-lib.js doesn't exist
            .mjs
              /workspace/node_modules/test-lib.mjs doesn't exist
            .ts
              /workspace/node_modules/test-lib.ts doesn't exist
            .mts
              /workspace/node_modules/test-lib.mts doesn't exist
        /workspace/node_modules/test-lib doesn't exist
      /node_modules doesn't exist or is not a directory

Config:

project.json

...
    "build": {
  "executor": "@nx/webpack:webpack",
  "dependsOn": ["lint"],
  "outputs": ["{options.outputPath}"],
  "options": {
    "target": "node",
    "compiler": "tsc",
    "outputPath": "dist/apps/my-express-api",
    "main": "apps/my-express-api/src/main.ts",
    "tsConfig": "apps/my-express-api/tsconfig.app.json",
    "assets": ["apps/my-express-api/src/assets"],
    "isolatedConfig": true,
    "webpackConfig": "apps/my-express-api/webpack.config.cjs",
  }
},
...

jest.config.json

/* eslint-disable */
export default {
  displayName: 'new-app-server',
  preset: '../../jest.preset.js',
  testEnvironment: 'node',
  transform: {
    '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
  },
  moduleFileExtensions: ['ts', 'js', 'html'],
  coverageDirectory: '../../coverage/apps/my-express-api',
};

package.json

{
  "name": "my-express-api",
  "version": "0.0.1",
  "type": "commonjs"
}

tsconfig.json

{
  "extends": "../../tsconfig.base.json",
  "files": [],
  "include": [],
  "references": [
    {
      "path": "./tsconfig.app.json"
    },
    {
      "path": "./tsconfig.spec.json"
    }
  ],
  "compilerOptions": {
    "module": "ES2022",
    "moduleResolution": "bundler",
    "esModuleInterop": true
  }
}

tsconfig.app.json

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "outDir": "../../dist/out-tsc",
    "module": "ESNext",
    "strict": true,
    "types": ["node", "express"]
  },
  "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"],
  "include": ["src/**/*.ts"]
}

webpack.config.cjs

const path = require('path');
const nodeExternals = require('webpack-node-externals');

module.exports = {
  mode: 'development', // or 'production' for production mode
  entry: './apps/my-express-api/src/main.ts', // Entry point of your Express application
  output: {
    path: path.resolve(__dirname, '../../dist/apps/my-express-api'),
    filename: 'main.mjs',
    module: true,
    libraryTarget: 'module',
    chunkFormat: 'module',
    chunkFilename: 'main.mjs',
    library: {
      type: 'module',
    },
    environment: {
      module: true,
    },
  },
  node: {
    __dirname: 'node-module',
  },
  resolve: {
    extensions: ['.js', '.mjs', '.ts', '.mts'], // Support both .js and .mjs extensions for ESM
    extensionAlias: {
      '.js': ['.ts', '.js'],
      '.mjs': ['.mts', '.mjs'],
      '.cjs': ['.cts', '.cjs'],
    },
  },
  target: 'node', // Important for server-side applications
  externals: [nodeExternals({ importType: 'module' })], // Exclude node_modules from the bundle
  externalsPresets: {
    node: true,
  },
  module: {
    rules: [
      // Add any necessary loaders or rules here
      { test: /\.([cm]?ts|tsx)$/, loader: 'ts-loader' },
    ],
  },
  experiments: {
    outputModule: true,
  },
  plugins: [
    // Add any necessary plugins here
  ],
};

Attempt 3

Using @nx/esbuild:esbuild

I thought I had solved it with this solution. If I import esm libraries (e.g. nanoid v4+) then I no longer get the Error [ERR_REQUIRE_ESM] error and I am able to build/serve my application.

The problem occurs when I try to import a workspace js library

Error:

error TS2307: Cannot find module '@myorg/mylib' or its corresponding type declarations.

Config:

project.json

...
  "build": {
  "executor": "@nx/esbuild:esbuild",
  "outputs": ["{options.outputPath}"],
  "options": {
    "outputPath": "dist/apps/my-express-api",
    "main": "apps/my-express-api/src/main.ts",
    "tsConfig": "apps/my-express-api/tsconfig.app.json",
    "assets": [],
    "generatePackageJson": true,
    "format": ["esm"],
    "platform": "node",
    "bundle": true
  }
},
...

package.json

{
  "name": "my-express-api",
  "version": "0.0.1",
  "type": "module"
}

tsconfig.app.json

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    //    "outDir": "../../../dist/out-tsc",
    "outDir": "dist",
    "lib": ["ES2021"],
    "module": "NodeNext",
    "target": "ES2021",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "moduleResolution": "NodeNext",
    "sourceMap": true,
    "declaration": true,
    "declarationMap": true,
    "baseUrl": ".",
    "resolveJsonModule": true,
    "types": ["node", "express"]
  },
  "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"],
  "include": ["src/**/*.ts"],
}

tsconfig.base.json

...
    "paths": {
      "@myorg/mylib": ["libs/mylib/src/index.ts"],
    }
...

Source code examples


Edits

  • Updated sandbox link
  • Added detailed error message
  • Added attempt 2 details
  • Added attempt 3 details
  • Added link to Github repo with all attempts and more
Trophic answered 13/2, 2024 at 2:4 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.