Skip to content

Error: require() of ES Modules Not Supported when Importing node-fetch

Problem Statement

When working with Node.js, you might encounter the error: "Error [ERR_REQUIRE_ESM]: Must use import to load ES Module" when trying to use require() to import the node-fetch package. This occurs because starting with version 3.0.0, node-fetch became an ES Module (ESM)-only package, which means it cannot be imported using CommonJS require() syntax.

The error message typically looks like this:

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module
require() of ES modules is not supported.

This issue commonly affects developers who:

  • Are using Node.js versions that don't fully support ESM
  • Have existing codebases using CommonJS syntax
  • Recently upgraded node-fetch to version 3+ without updating their import syntax

Solution Overview

There are several approaches to resolve this issue:

  1. Use ESM syntax (recommended for new projects)
  2. Downgrade to node-fetch v2 (for legacy CommonJS projects)
  3. Use dynamic import() (for mixed codebases)
  4. Use Node.js built-in fetch (for Node.js 18+)
  5. Alternative HTTP clients (if flexibility is needed)

Detailed Solutions

The most straightforward solution is to convert your project to use ES Modules:

Step 1: Add "type": "module" to your package.json:

json
{
  "type": "module",
  // other package.json content
}

Step 2: Update your import statements to use ESM syntax:

js
import fetch from 'node-fetch';

Step 3: Run your script directly without the esm package:

bash
node server.js

2. Downgrade to node-fetch v2

If you need to maintain CommonJS compatibility, downgrade to the last CommonJS-compatible version:

bash
npm install node-fetch@2.6.7

Then continue using require syntax:

js
const fetch = require('node-fetch');

WARNING

Version 2.x will only receive critical bug fixes, so this should be considered a temporary solution.

3. Use Dynamic Import()

For mixed codebases that need to maintain CommonJS structure but use ESM packages:

js
// Using a wrapper function
const fetch = (...args) => 
  import('node-fetch').then(({default: fetch}) => fetch(...args));

// With TypeScript typing
import { RequestInfo, RequestInit, Response } from 'node-fetch';

const fetch = async (url: RequestInfo, init?: RequestInit): Promise<Response> => {
  const { default: fetch } = await import('node-fetch');
  return fetch(url, init);
};

4. Use Node.js Built-in Fetch (Node.js 18+)

Node.js 18+ includes a built-in fetch implementation:

js
// No installation needed for Node.js 18+
const response = await fetch('https://api.example.com/data');
const data = await response.json();

INFO

The built-in fetch is still experimental in Node.js 18 but stable in later versions.

5. Alternative HTTP Clients

Consider using alternative HTTP clients that support CommonJS:

Using axios:

bash
npm install axios
js
const axios = require('axios');

const response = await axios.get('https://api.example.com/data');

Using got:

bash
npm install got
js
const got = require('got');

const response = await got.get('https://api.example.com/data');

Code Examples

ESM Implementation

json
// package.json
{
  "type": "module",
  "scripts": {
    "start": "node server.js"
  },
  "dependencies": {
    "node-fetch": "^3.0.0"
  }
}
js
// server.js
import fetch from 'node-fetch';

async function getData() {
  try {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error('Error:', error);
  }
}

getData();

CommonJS with Dynamic Import

js
// fetch-wrapper.js
const fetch = (...args) => 
  import('node-fetch').then(({default: fetch}) => fetch(...args));

module.exports = { fetch };
js
// main.js
const { fetch } = require('./fetch-wrapper');

async function main() {
  const response = await fetch('https://api.example.com/data');
  const data = await response.json();
  console.log(data);
}

main();

Migration Guide

When transitioning from CommonJS to ESM, consider these changes:

  1. File extensions: Use .mjs for ES Modules or set "type": "module" in package.json
  2. Import syntax: Replace require() with import
  3. __dirname replacement: Use import.meta.url instead
    js
    import { fileURLToPath } from 'url';
    import { dirname } from 'path';
    
    const __filename = fileURLToPath(import.meta.url);
    const __dirname = dirname(__filename);

Best Practices

  1. Check Node.js version: Ensure you're using a Node.js version that fully supports ESM (14.8.0+ for stable support)
  2. Consistent module system: Avoid mixing ESM and CommonJS in the same project when possible
  3. Update package.json: Clearly specify your module system with "type": "module" or "type": "commonjs"
  4. Consider future compatibility: The Node.js ecosystem is moving toward ESM, so consider migrating when feasible

TIP

For large existing projects, consider a gradual migration by converting one file at a time or using the dynamic import approach for ESM packages.

Troubleshooting

If you continue to experience issues:

  1. Clear node_modules and package-lock.json:

    bash
    rm -rf node_modules package-lock.json
    npm install
  2. Check for multiple fetch implementations: Ensure you don't have conflicting HTTP client packages

  3. Verify Node.js version:

    bash
    node -v
  4. Check for TypeScript configuration: If using TypeScript, ensure your tsconfig.json is configured correctly for your module system

By understanding the shift from CommonJS to ES Modules in the Node.js ecosystem and applying the appropriate solution for your project, you can resolve the require() of ES Modules error and ensure compatibility with modern packages like node-fetch v3+.