Error: require() of ES modules is not supported when importing node-fetch
Asked Answered
T

15

129

I'm creating a program to analyze security camera streams and got stuck on the very first line. At the moment my .js file has nothing but the import of node-fetch and it gives me an error message. What am I doing wrong?

Running Ubuntu 20.04.2 LTS in Windows Subsystem for Linux.

Node version:

user@MYLLYTIN:~/CAMSERVER$ node -v
v14.17.6

node-fetch package version:

user@MYLLYTIN:~/CAMSERVER$ npm v node-fetch

[email protected] | MIT | deps: 2 | versions: 63
A light-weight module that brings Fetch API to node.js
https://github.com/node-fetch/node-fetch

keywords: fetch, http, promise, request, curl, wget, xhr, whatwg

dist
.tarball: https://registry.npmjs.org/node-fetch/-/node-fetch-3.0.0.tgz
.shasum: 79da7146a520036f2c5f644e4a26095f17e411ea
.integrity: sha512-bKMI+C7/T/SPU1lKnbQbwxptpCrG9ashG+VkytmXCPZyuM9jB6VU+hY0oi4lC8LxTtAeWdckNCTa3nrGsAdA3Q==
.unpackedSize: 75.9 kB

dependencies:
data-uri-to-buffer: ^3.0.1 fetch-blob: ^3.1.2         

maintainers:
- endless <[email protected]>
- bitinn <[email protected]>
- timothygu <[email protected]>
- akepinski <[email protected]>

dist-tags:
latest: 3.0.0        next: 3.0.0-beta.10  

published 3 days ago by endless <[email protected]>

esm package version:

user@MYLLYTIN:~/CAMSERVER$ npm v esm

[email protected] | MIT | deps: none | versions: 140
Tomorrow's ECMAScript modules today!
https://github.com/standard-things/esm#readme

keywords: commonjs, ecmascript, export, import, modules, node, require

dist
.tarball: https://registry.npmjs.org/esm/-/esm-3.2.25.tgz
.shasum: 342c18c29d56157688ba5ce31f8431fbb795cc10
.integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==
.unpackedSize: 308.6 kB

maintainers:
- jdalton <[email protected]>

dist-tags:
latest: 3.2.25  

published over a year ago by jdalton <[email protected]>

Contents of the .js file (literally nothing but the import):

user@MYLLYTIN:~/CAMSERVER$ cat server.js 
import fetch from "node-fetch";

Result:

user@MYLLYTIN:~/CAMSERVER$ node -r esm server.js 
/home/user/CAMSERVER/node_modules/node-fetch/src/index.js:1
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /home/user/CAMSERVER/node_modules/node-fetch/src/index.js
require() of ES modules is not supported.
require() of /home/user/CAMSERVER/node_modules/node-fetch/src/index.js from /home/user/CAMSERVER/server.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename index.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /home/user/CAMSERVER/node_modules/node-fetch/package.json.

    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1089:13) {
  code: 'ERR_REQUIRE_ESM'
}
user@MYLLYTIN:~/CAMSERVER$ 
Totalizator answered 3/9, 2021 at 8:1 Comment(11)
They are two types of module syntax for nodejs. The commonJS syntaxe, that uses require and module.exports, and the ES syntax, that use import * from "path" style of module. By default, nodejs will try to load modules with the CommonJS syntax. If you want to use the ES syntax, you must specify "type":"module" in your package.json. But you can't mix them up. You can use one syntax, but not both.Incendiarism
@Ariart I am using ES syntax in my code and node-fetch does have "type": module in it's package.jsonTotalizator
Is it specified in your project package.json ?Incendiarism
@Ariart I didn't even have a package.json, created one with nothing but "type": "module" in it and that worked! Thank you! Is there a way to set this inline when running node instead of defining it in package.json?Totalizator
Yes, you can run node with a flag. From memory, it's something like --input-type=moduleIncendiarism
what is the answer for thisCommitment
I'm using typescript, so can someone tell me why ```import fetch from node-fetch doesn't work for me, as im already working with es6+ syntaxView
@Yash, check your tsconfig.json for compilerOptions.module, which is probably commonjs. If that's true, even though you're writing ES6-style import statements, TS is transpiling them to CJS.Alysa
@RoopeKuisma, there is a command line flag but also if you write your ES module code (using import / export statements etc) in a file with an .mjs extension, modern Node will treat it as ESM automatically.Alysa
This ES Module stuff is such unnecessary drama. There's no silver bullet solution. I'm already using type: "module" and import for other libs, but p-limit in particular has this issue regardless. I'm sure it has something to do with my project setup, but you know, this is supposed to be easy. Just let me require -_-Predigestion
I found the best solution was to just use axios. Libraries that cause me to yak shave are candidates for the trash.Mediator
F
18

node-fetch v3 is ESM-only: https://github.com/node-fetch/node-fetch#loading-and-configuring-the-module. The esm module you’re adding is for adding ESM compatibility, but it’s unnecessary now that Node 12+ supports ESM natively; and it doesn’t work with ESM-only packages like node-fetch 3+.

To fix your issue:

  1. Remove the esm package.
  2. Add "type": "module" to your package.json.

And that’s it. Then when you run node server.js it should work.

Frankfrankalmoign answered 25/9, 2021 at 20:53 Comment(4)
I think it's still suggested to use the .mjs extension when you're writing ESM code for Node, but I'm having trouble finding a reference.Alysa
.mjs is an option, but not required. nodejs.org/api/packages.html#determining-module-systemFrankfrankalmoign
after adding that line its telling me it doesn't recognize the .ts file extensionRania
.ts is the extension for a TypeScript file, which Node doesn’t support out-of-the-box. Look into ts-node to add support for TypeScript to Node.Frankfrankalmoign
S
67

From the Upgrade Guide

node-fetch was converted to be a ESM only package in version 3.0.0-beta.10. node-fetch is an ESM-only module - you are not able to import it with require.

Alternatively, you can use the async import() function from CommonJS to load node-fetch asynchronously:

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

or you stay on v2 as they say in the README

node-fetch is an ESM-only module - you are not able to import it with require. We recommend you stay on v2 which is built with CommonJS unless you use ESM yourself. We will continue to publish critical bug fixes for it.

EDIT

I saw that in the doc, used it, and the script crashes with :

error TS2556: A spread argument must either have a tuple type or be passed to a rest parameter.

Since you are using typescript you should do something like this

import { RequestInfo, RequestInit } from "node-fetch";

const fetch = (url: RequestInfo, init?: RequestInit) =>  import("node-fetch").then(({ default: fetch }) => fetch(url, init));
Satisfactory answered 26/9, 2021 at 2:51 Comment(9)
I saw that in the doc, used it, and the script crashes with : error TS2556: A spread argument must either have a tuple type or be passed to a rest parameter. Node.js 14.17.1Bigname
@JeremyThille I have updated my answer. I haven't tested it yet, so please let me know if it works.Mogador
Thank you very much. I tried exactly that, but I'm getting Error [ERR_REQUIRE_ESM]: Must use import to load ES Module. require() of ES modules is not supported. index.ts is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules. Instead rename index.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from node_modules/node-fetch/package.json. It keeps saying I'm using require, but I'm using import. Running my TS file with ts-node-dev index.ts.Bigname
I rolled back to node-fetch v2 and it works fine, though.Bigname
@JeremyThille did you ever find out why? I have the same as you - I'm already using import.Dandy
Nope... Just rolled back to node-fetch v2, as I can't make v3 work.Bigname
It doesn't work because of the "import nodeFetch..." statement. You should only import types not the implementation. this should work: import { RequestInfo, RequestInit } from 'node-fetch'; const fetch = (url: RequestInfo, init?: RequestInit) => import('node-fetch').then(({ default: fetch }) => fetch(url, init));Stereoscope
Unfortunatelly this didn't worked for me either... Instead I used cross-fetch and it works without a problem.Erlindaerline
The TypeScript workaround doesn't work (at least with my configuration using TS 4.8.2) because it emits const fetch = (url, init) => Promise.resolve().then(() => __importStar(require("node-fetch"))).then(({ default: fetch }) => fetch(url, init)); which still uses require().Deadman
E
34

To fix this, I downgraded node-fetch to the latest version 2, which right now is 2.6.6. This is the script I ran:

yarn add node-fetch@^2.6.6

or

npm install node-fetch@^2.6.6

I also added these compiler options:

{
  "compilerOptions": { "allowJs": true, "outDir": "./dist" },
}
Equinox answered 16/1, 2022 at 5:14 Comment(3)
didnt work for me, using node-fetch 3.xSantoyo
Where exactly did you add compiler options?Energid
@Energid tsconfig fileGiefer
F
18

node-fetch v3 is ESM-only: https://github.com/node-fetch/node-fetch#loading-and-configuring-the-module. The esm module you’re adding is for adding ESM compatibility, but it’s unnecessary now that Node 12+ supports ESM natively; and it doesn’t work with ESM-only packages like node-fetch 3+.

To fix your issue:

  1. Remove the esm package.
  2. Add "type": "module" to your package.json.

And that’s it. Then when you run node server.js it should work.

Frankfrankalmoign answered 25/9, 2021 at 20:53 Comment(4)
I think it's still suggested to use the .mjs extension when you're writing ESM code for Node, but I'm having trouble finding a reference.Alysa
.mjs is an option, but not required. nodejs.org/api/packages.html#determining-module-systemFrankfrankalmoign
after adding that line its telling me it doesn't recognize the .ts file extensionRania
.ts is the extension for a TypeScript file, which Node doesn’t support out-of-the-box. Look into ts-node to add support for TypeScript to Node.Frankfrankalmoign
M
14

I always had errors with previous solutions, this is what worked every single time for me

npm i -D node-fetch

const _importDynamic = new Function('modulePath', 'return import(modulePath)');

export const fetch = async function (...args: any) {
    const {default: fetch} = await _importDynamic('node-fetch');
    return fetch(...args);
}
Midterm answered 28/3, 2022 at 7:4 Comment(2)
THANK YOU! Of all the other answers, this is the only one that works for me.Phraseologist
Why does this work, what does it do?Zaneta
A
7

Use ESM syntax, also use one of these methods before running the file.

  1. specify "type":"module" in package.json
  2. Or use this flag --input-type=module when running the file
  3. Or use .mjs file extension
  4. Otherwise load the package using dynamic import()
Aquarius answered 22/9, 2021 at 5:22 Comment(3)
In what file?sssEnergid
This seems to taint my code. If I want to use a new package and it is ESM, it seems to not work unless I make my module ESM. But then all my other modules that use it throw the same error, which requires that I make them all ESM. This can't be right... I'm not going to change all my modules to ESM just to use one new ES module, there has to be a better way.Incorrupt
You could technically use the dynamic importAquarius
E
4

So far, the offered solutions to this question are:

  1. downgrade to an earlier version of the installed package.
  2. convert your project to a pure esm project.
  3. use dynamic import.

There is another solution not yet mention -- install a hook called esm-hook.

npm install esm-hook

This solution does not require you to change any of your code; you simply require the hook at the top of your javascript file:

require("esm-hook");  // <--- add this to the top of your file.

const fetch = require('node-fetch').default

fetch("https://hacker-news.firebaseio.com/v0/item/8863.json?print=pretty")
  .then(res => res.json())
  .then(json => console.log(json))
  .catch(console.error);

A quick look at the code shows that it uses esbuild to transpile the esm module to commonjs. This might impact performance if the module you consume is large.

Emissivity answered 28/7, 2023 at 20:36 Comment(0)
H
3

Since the question was really about requiring node-fetch and there are definitely reasons that a developer might need to use require vs import, the answer to import the module using import() is somewhat accurate but isn't complete, because it ignores the fact that using import is async and if you're using node-fetch in your code in multiple places you're going to have a bit of an async mess all over the place whenever you want to use node-fetch, so the answer is very incomplete IMHO. A more complete answer would include a better how-to. Something like the following:

You need to use import() rather than require because this is an ES module. To avoid an async mess, you'll want to await the importing of this module once and then be able to use it wherever else you need it. To do that, create a module that imports any of your ES-only modules something like this:

"use strict";

let got;
let fetch;

module.exports.load = async function() {
    queueMicrotask(function() {
        // any ESM-only module that needs to be loaded can be loaded here, just add import for each using specific structure of each
        import("got").then(({default: Got}) => got = Got );
        fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args));
    });

    while(
        // check each module to see if it's been loaded here
        !got || !got.get || !fetch || typeof fetch !== "function"
         ) {
        // waiting for modules to load
        console.log("Waiting for ES-Only modules to load...");
        await new Promise((resolve)=>setTimeout(resolve, 1000));
    }
    module.exports.got = got;
    module.exports.fetch = fetch;
    console.log("ES-Only modules finished loading!");
}

This then allows you to call the await the loading of the ES-Only modules once and then use the module later by grabbing it from your intermediary module like this:

"use strict";

const esmModules = require("esm_modules"); // or whatever you called the intermiary module
async function doMyFetching(url, options) {
   const fetch = esmModules.fetch;
   const result = await fetch(url, options)
}

The nice part of doing it this way is that you know that your ES-Only modules are loaded and you know that once they're loaded you can use them at will throughout the rest of your code in a simple and clean way that doesn't force you to add additional async logic all over the place whenever you need the module.

Harmonica answered 3/1, 2022 at 16:56 Comment(1)
The question was not about requiring node-fetch, I did not use require and did not want to. Thanks for the informative reply tho might help someoneTotalizator
M
3

In case someone is solving this issue in 2022:

Recent versions of nodejs already includes global fetch api. It is still an experimental feature and may be not ready to be used in production, but if you are writing some basic script or doing some sandbox project, you should definitely try it. Works without issues for me.

This is an official announcement and some examples here and here

Mendy answered 12/12, 2022 at 22:45 Comment(0)
G
2

Many times that error happens through the current version of fetch what we can do to solve it, is to install a previous version I tried it and it worked for me.

to install the previous version: npm i [email protected] or whatever you prefer.

enter image description here

Gentianaceous answered 8/2, 2022 at 17:11 Comment(1)
All questions and answers on the main Stack Overflow site are required to be in English.Diary
M
1

Here is in Typescript, this will install to the global scope, just import the file and fetch ready to use:

/* filename: fetch.ts */

if (!globalThis.fetch) {
  import('node-fetch').then(({ default: fetch, Headers, Request, Response }) => {
    Object.assign(globalThis, { fetch, Headers, Request, Response })
  })
}
Managerial answered 27/9, 2022 at 3:42 Comment(0)
A
1

If you use typescript in yout project, you can now use tsx package. tsx is a CLI command (alternative to node) for seamlessly running TypeScript & ESM, in both commonjs & module package types.

Attribute answered 26/6, 2023 at 0:1 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Piping
P
0

As said others this error because the node-fetch package has been converted to be an ESM-only package in version 3,so to solve the node-fetch error

  1. use a dynamic import to import the node-fetch package
  2. downgrade the version of the package

This link is a comprehensive explanation about Error [ERR_REQUIRE_ESM]: require() of ES [node-fetch Module ] not supported.

node-fetch Error [ERR_REQUIRE_ESM]: require() of ES Module not supported

According to nodejs v18 doc in node v18 fetch package is built in so if upgrade node to v18 you do not need to install node-fetch separately.

Pending answered 29/11, 2022 at 12:48 Comment(1)
Please avoid link only answers. Answers that are "barely more than a link to an external site” may be deleted.Vegetal
P
0

@Cizia's answer worked for me. With TypeScript, I wanted a bit more type safety, so I merged it with @René Link's answer.

import { RequestInfo, RequestInit, Response } from 'node-fetch'

const _importDynamic = new Function('modulePath', 'return import(modulePath)')

export const nodeFetch = async function (url: URL | RequestInfo, init?: RequestInit): Promise<Response> {
  const { default: fetch } = await _importDynamic('node-fetch')
  return fetch(url, init)
}
Prosody answered 5/7, 2023 at 18:43 Comment(0)
T
0

The best option is to use something else like gaxios https://github.com/googleapis/gaxios .

For example:

console.log('downloading using gaxios')
/** @type {import('gaxios').GaxiosResponse<ArrayBuffer>} */
// called inside an async function
const result = await request({
            url: downloadUrl,
            responseType: 'arraybuffer',
            // avoid error saying failed to verify first certificate
            agent: new https.Agent({ rejectUnauthorized: false })
});
const arrayBuff = result.data
const buff = Buffer.from(arrayBuff)

But if you really must use node-fetch, then you have two other options:

  1. You can install a module that works like fetch but supports commonjs eg isomorphic-fetch

  2. You can install a lower-version of node-fetch.

First uninstall the node-fetch version that is only allowing ESM modules:

npm uninstall node-fetch

Then install the old node-fetch version that supports commonjs:

npm i [email protected]  
Tunisia answered 15/8, 2023 at 7:27 Comment(0)
B
0

Try using this in package.json

 "resolutions": {
    "jackspeak": "2.1.1"
  }

source: https://github.com/storybookjs/storybook/issues/22431#issuecomment-1803383355

Barleycorn answered 8/12, 2023 at 9:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.