You write a simple script that uses fetch(), exactly like you do in the browser every day. You run it with Node.js 16, and instead of data you get an error: ReferenceError: fetch is not defined. Nothing in your code looks wrong, so the error feels unfair.
It is actually a version story, not a coding mistake. The global fetch function arrived in Node much later than it arrived in browsers, and Node 16 simply does not include it by default. In this guide, you will learn why that is, and three working fixes — upgrading Node, using the built-in HTTPS module, and installing a fetch library — with clear steps so you can pick the right one for your project.
Quick Answer
“ReferenceError: fetch is not defined” in Node.js 16 happens because the global Fetch API was only added as a stable built-in in Node 18 and later, so Node 16 has no fetch function. Fix it by upgrading to Node 18 or newer, installing the node-fetch package and importing it, or making the request with Node’s built-in https module without any new dependency.
Why Node 16 Does Not Have Fetch
Browsers and Node.js are separate runtimes. They share the JavaScript language, but each provides its own set of built-in tools. Browsers added fetch many years ago as the modern way to make network requests. Node, which grew up on the server, historically expected developers to use its own http and https modules or install a library.
Node eventually adopted the same Fetch API so code could be shared between browser and server. An experimental version appeared behind a flag in Node 17.5, it was enabled by default in Node 18, and it matured in later releases. Node 16, which is now past its end-of-life, never received it as a standard built-in. When your script calls fetch() on Node 16, the engine searches for that name, finds nothing, and throws a ReferenceError.
That error type is a useful clue. A ReferenceError means the name itself does not exist in this environment. It is different from a network failure or a bad URL, which would only appear after fetch was found and actually ran. So before debugging your URL or your API, you first have to give the runtime a working fetch — or stop depending on one.
Check Your Version First
- Open a terminal in your project folder.
- Run
node --versionand read the output. If it starts withv16, this guide applies directly to you. - Run
node -e "console.log(typeof fetch)". On Node 16 this printsundefined, confirming that the global is missing rather than broken. - Check whether your project or hosting provider pins a Node version in package.json under
engines, or in a version file such as.nvmrc, so your fix targets the version that actually runs in production, not only on your machine.
Knowing the exact runtime matters because a fix that works locally can fail after deployment if the server still runs Node 16. Decide the fix for the oldest environment you must support.
Fix 1: Upgrade to Node 18 or Newer
This is the best fix whenever you are allowed to change the runtime. It removes the problem permanently, needs no extra dependency, and also gives you security updates that Node 16 no longer receives.
- Install a current long-term support release, such as Node 20 or Node 22, from the official installer or through a version manager like nvm.
- If you use nvm, run
nvm install 20followed bynvm use 20, then confirm withnode --version. - Update any pinned version in
.nvmrc, theenginesfield, and your hosting or CI settings so every environment matches. - Run
node -e "console.log(typeof fetch)"again. It should now printfunction. - Run your script unchanged. The original
fetch()code should work as written, includingawait response.json()inside an async function.
Upgrading is also the safest long-term choice for teams. Staying on an end-of-life runtime accumulates missing features and unpatched vulnerabilities, and this fetch error is often the first visible symptom of that gap. If version confusion affects several projects, our overview of common Node.js version problems and their solutions explains how to standardize versions across machines and servers.
Fix 2: Install node-fetch and Import It
If you must stay on Node 16 — for example, a legacy server you cannot change yet — install a library that provides fetch for you. The most widely used option is node-fetch.
- In your project folder, run
npm install node-fetch@2. Version 2 is the right choice for Node 16 projects that use CommonJSrequire. - At the top of your file, add
const fetch = require("node-fetch");before any code that calls fetch. - Use fetch exactly as before, for example
const response = await fetch(url); const data = await response.json();inside an async function. - Commit the updated package.json and package-lock.json so other machines install the same dependency.
A version note matters here. node-fetch version 3 is ESM-only, which means require("node-fetch") fails in a CommonJS project and produces a different error. On Node 16 with require, stay on version 2. In an ES module project with "type": "module", you can install the latest version and use import fetch from "node-fetch"; instead. Matching the package version to your module system prevents trading one confusing error for another.
Fix 3: Use the Built-In https Module
If you cannot upgrade and do not want any new dependency, Node has always been able to make requests with its built-in modules. The style is older and more verbose, but it works on every Node version, including 16.
- Import the module with
const https = require("https");— usehttponly for plain HTTP addresses. - Call
https.get(url, (response) => { ... })and collect the response chunks in thedataevent. - On the
endevent, join the chunks and parse them withJSON.parse(...)if the API returns JSON. - Add an
errorhandler on the request so network failures are logged instead of crashing silently.
This approach has no promise-based API by default, so many developers wrap it in a Promise to keep the rest of the code clean. It is a solid fallback for small scripts and restricted environments, but for larger projects Fix 1 or Fix 2 will be easier to maintain.
Common Mistakes and Tips
- Installing node-fetch but forgetting the import: The package does not create a global. Your file must import or require it before the first fetch call, in every file that uses fetch.
- Installing the wrong major version: Version 3 with
requirethrows an ESM error. Check your module system first, then choose version 2 for CommonJS on Node 16 or the latest for ESM. - Fixing only your laptop: If CI, Docker, or your host still runs Node 16, the error will return at deploy time. Update version pins everywhere, or use the library fix consistently.
- Enabling an experimental flag and calling it done: Later Node lines exposed fetch behind
--experimental-fetch. Node 16 does not offer a reliable built-in fetch this way, so do not build a production fix around flags for this version. - Ignoring end-of-life risk: Node 16 no longer receives security updates. Even if the library fix works today, plan the upgrade as the real solution rather than a someday task.
For authoritative details, check the official Node.js download and release page to choose a supported long-term release, and review the node-fetch package documentation on npm for the version and import instructions that match your module system.
Conclusion
“Fetch is not defined” in Node.js 16 is a missing feature, not a mistake in your request code. Node 16 predates the built-in Fetch API, so the name simply does not exist in that runtime. You have three solid options: upgrade to Node 18 or newer for the clean, permanent fix; install node-fetch version 2 and import it when you must remain on Node 16 with CommonJS; or fall back to the built-in https module when you cannot add dependencies. Verify the result by checking typeof fetch and running your script in the same version your production environment uses, and the error will be gone for good.
Frequently Asked Questions
Which Node version added fetch by default?
Node 18 enabled the global Fetch API by default, building on an experimental appearance in Node 17.5. Later long-term releases such as Node 20 and 22 include it as a standard, stable part of the runtime.
Can I use fetch in Node 16 without installing anything?
Not the global fetch function itself. Without a package, your built-in option is the http or https module, which can make the same requests in an older callback style. For real fetch syntax on Node 16, install node-fetch.
Why does require(“node-fetch”) throw an error?
You most likely installed version 3, which is ESM-only, in a CommonJS project. Install version 2 with npm install node-fetch@2 for require, or convert the file to an ES module and use an import statement with the latest version.
Is node-fetch identical to browser fetch?
It closely follows the same API, so common code like awaiting the response and calling response.json() works the same way. Small differences exist in some edge cases, so check the package documentation if you use advanced options.
Should I upgrade Node just for fetch?
Yes, if you control the environment. Node 16 is end-of-life and receives no security updates, so upgrading solves the fetch error and removes a growing security and compatibility risk at the same time. Use the library fix only as a bridge when an upgrade is temporarily impossible.


Leave a Reply