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:
- Use ESM syntax (recommended for new projects)
- Downgrade to node-fetch v2 (for legacy CommonJS projects)
- Use dynamic import() (for mixed codebases)
- Use Node.js built-in fetch (for Node.js 18+)
- Alternative HTTP clients (if flexibility is needed)
Detailed Solutions
1. Use ESM Syntax (Recommended)
The most straightforward solution is to convert your project to use ES Modules:
Step 1: Add "type": "module"
to your package.json
:
{
"type": "module",
// other package.json content
}
Step 2: Update your import statements to use ESM syntax:
import fetch from 'node-fetch';
Step 3: Run your script directly without the esm
package:
node server.js
2. Downgrade to node-fetch v2
If you need to maintain CommonJS compatibility, downgrade to the last CommonJS-compatible version:
npm install node-fetch@2.6.7
Then continue using require syntax:
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:
// 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:
// 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:
npm install axios
const axios = require('axios');
const response = await axios.get('https://api.example.com/data');
Using got:
npm install got
const got = require('got');
const response = await got.get('https://api.example.com/data');
Code Examples
ESM Implementation
// package.json
{
"type": "module",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"node-fetch": "^3.0.0"
}
}
// 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
// fetch-wrapper.js
const fetch = (...args) =>
import('node-fetch').then(({default: fetch}) => fetch(...args));
module.exports = { fetch };
// 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:
- File extensions: Use
.mjs
for ES Modules or set"type": "module"
in package.json - Import syntax: Replace
require()
withimport
- __dirname replacement: Use
import.meta.url
insteadjsimport { fileURLToPath } from 'url'; import { dirname } from 'path'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename);
Best Practices
- Check Node.js version: Ensure you're using a Node.js version that fully supports ESM (14.8.0+ for stable support)
- Consistent module system: Avoid mixing ESM and CommonJS in the same project when possible
- Update package.json: Clearly specify your module system with
"type": "module"
or"type": "commonjs"
- 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:
Clear node_modules and package-lock.json:
bashrm -rf node_modules package-lock.json npm install
Check for multiple fetch implementations: Ensure you don't have conflicting HTTP client packages
Verify Node.js version:
bashnode -v
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+.