ReferenceError: fetch is not defined in TypeScript (Fixed)

ReferenceError: fetch is not defined in TypeScript means your compiled JavaScript is running somewhere that has no global fetch, usually Node.js older than 18 or a Jest jsdom test. Upgrade Node, or load node-fetch first:

node -v                     # v18.0.0 or later has a global fetch
npm install node-fetch      # only if you're stuck on an older runtime

Every example below ran on TypeScript 7.0.2 and Node.js v22.22.2 with strict mode, and I copied the output straight from those runs. No example calls the internet; they use data: URLs or a local server.

We’ll look at why the error happens, how to reproduce it, the separate compile-time error Cannot find name 'fetch', and the fixes for old Node, tests and server-side rendering.

Why fetch is not defined: TypeScript compiles, the runtime fails

TypeScript only checks types and emits JavaScript. It never adds fetch to your program. Whether fetch exists is decided by whatever runs that JavaScript: a browser, Node.js, Deno, Bun, or a test environment.

Browsers have had fetch for years. Node.js didn’t, and the Node.js docs give this history:

  • v17.5.0 and v16.15.0: fetch added, but only behind the --experimental-fetch flag
  • v18.0.0: global fetch on by default
  • v21.0.0: no longer experimental

So the same file can work in Chrome and crash on a build server running Node 16. That’s a runtime problem, which is why tsc stays quiet. If you’re new to how TypeScript code runs, my TypeScript global variables guide explains what lives on globalThis.

Reproduce ReferenceError: fetch is not defined on Node 22

I don’t keep Node 16 around, but Node 22 still accepts --no-experimental-fetch, which removes fetch from the global scope. That’s exactly what an old runtime looks like:

console.log("Node.js", process.version, "| typeof fetch:", typeof fetch);

fetch('data:application/json,{"order":1001,"city":"Denver","total":89.5}')
  .then((res) => res.json())
  .then((order) => console.log("fetched:", order));

Output:

Node.js v22.22.2 | typeof fetch: function
fetched: { order: 1001, city: 'Denver', total: 89.5 }
Node.js v22.22.2 | typeof fetch: undefined
C:\pyguides\check_fetch.js:3
fetch('data:application/json,{"order":1001,"city":"Denver","total":89.5}')
^

ReferenceError: fetch is not defined
    at Object.<anonymous> (C:\pyguides\check_fetch.js:3:1)
    at Module._compile (node:internal/modules/cjs/loader:1705:14)
    at Object..js (node:internal/modules/cjs/loader:1838:10)
    at Module.load (node:internal/modules/cjs/loader:1441:32)
    at Function._load (node:internal/modules/cjs/loader:1263:12)
    at TracingChannel.traceSync (node:diagnostics_channel:328:14)
    at wrapModuleLoad (node:internal/modules/cjs/loader:237:24)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:171:5)
    at node:internal/main/run_main_module:36:49

Node.js v22.22.2
Command Prompt running a TypeScript fetch example on Node.js 22, then the same file with --no-experimental-fetch throwing ReferenceError: fetch is not defined
Normal Node 22 fetches the order; with the flag, typeof fetch is undefined and the call throws.

Notice typeof fetch doesn’t throw; it returns "undefined". That makes it a safe feature check, a pattern I cover in type narrowing in TypeScript. The flag also removes Headers, Request, Response and FormData, and it’s gone from the Node 24 docs.

Fix TS2304: Cannot find name ‘fetch’

There’s a second, compile-time version of this problem. If your lib setting leaves out dom and you don’t load Node’s types, the compiler has never heard of fetch:

interface Order {
  id: number;
  city: string;
  total: number;
}

export async function getOrder(id: number): Promise<Order> {
  const res = await fetch(`https://api.example.com/orders/${id}`);
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const order = (await res.json()) as Order;
  console.log(`Order ${order.id}: $${order.total.toFixed(2)}`);
  return order;
}

Output:

order_api.ts(8,21): error TS2304: Cannot find name 'fetch'.
order_api.ts(11,3): error TS2584: Cannot find name 'console'. Do you need to change your target library? Try changing the 'lib' compiler option to include 'dom'.
Command Prompt showing tsc with --lib es2024 reporting TS2304 Cannot find name 'fetch' and TS2584 for console
With only es2024 in lib, both fetch and console are unknown.

The fix depends on where the code runs. Browser code wants the dom library; Node-only code wants @types/node (I used version 22), which declares a global fetch too:

interface Order {
  id: number;
  city: string;
  total: number;
}

export async function getOrder(id: number): Promise<Order> {
  const res = await fetch(`https://api.example.com/orders/${id}`);
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const order = (await res.json()) as Order;
  console.log(`Order ${order.id}: $${order.total.toFixed(2)}`);
  return order;
}

Output:

lib es2024,dom: no errors
lib es2024 + types node: no errors
Command Prompt showing the same TypeScript fetch file compiling with --lib es2024,dom and with --types node
Either setting makes fetch known to the compiler.

In tsconfig.json you only need one of these lines, depending on your target. It’s the same file you edit when you import a JSON file in TypeScript:

{
  "compilerOptions": {
    "target": "ES2024",
    "lib": ["ES2024", "DOM"],   // browser or shared code
    "types": ["node"]           // Node code: npm install -D @types/node
  }
}

Fixing the types doesn’t add fetch at runtime, though. A clean compile on Node 16 still crashes. Also avoid silencing the error with declare const fetch: any; you’d lose every type check on the response, as my TypeScript any type post shows.

How do I fix fetch is not defined in Node.js?

Option 1: upgrade to Node.js 18 or later

Node 18 and 20 are past end of life, so I’d move straight to Node 22 or 24. You should run node -v in the same terminal, Docker image or CI job that throws the error, because version managers often hide an older Node.

Option 2: import node-fetch or undici

When upgrading isn’t possible, import a fetch implementation instead of relying on the global. Version 3 of node-fetch is ESM-only, so use import (or version 2 with require):

import fetch from "node-fetch";

console.log("global fetch:", typeof globalThis.fetch);

const res = await fetch('data:application/json,{"city":"Austin","state":"TX","total":1249.5}');
const order = (await res.json()) as { city: string; state: string; total: number };
console.log(`node-fetch: ${order.city}, ${order.state} $${order.total.toFixed(2)}`);

Output:

global fetch: undefined
node-fetch: Austin, TX $1249.50

That run uses --no-experimental-fetch, so the global is gone and the import still works. The undici package, which powers Node’s built-in fetch, exports fetch the same way. Reading the JSON safely is covered in TypeScript promises.

Fix fetch is not defined in Jest tests and SSR

Jest’s jsdom environment builds its globals from jsdom, which doesn’t implement fetch. So tests can fail even on Node 22. If the code under test doesn’t need the DOM, set testEnvironment: "node".

Otherwise, install a polyfill before the tests run. This version only fills the gap when fetch is missing, and I ran it both ways:

import { createServer } from "node:http";

// Install fetch only when the runtime doesn't have it
if (typeof globalThis.fetch !== "function") {
  const nf = await import("node-fetch");
  Object.assign(globalThis, { fetch: nf.default, Headers: nf.Headers, Request: nf.Request, Response: nf.Response });
  console.log("fetch polyfilled with node-fetch");
} else {
  console.log("using the built-in fetch");
}

// A local API so the example never depends on the internet
const server = createServer((req, res) => {
  res.setHeader("Content-Type", "application/json");
  res.end(JSON.stringify([{ id: 1001, city: "Denver", total: 89.5 }, { id: 1002, city: "Tampa", total: 142.25 }]));
}).listen(0);
const { port } = server.address() as { port: number };

const res = await fetch(`http://localhost:${port}/api/orders`);
const orders = (await res.json()) as { id: number; city: string; total: number }[];
for (const o of orders) console.log(`#${o.id} ${o.city.padEnd(7)} $${o.total.toFixed(2)}`);
server.close();

Output:

using the built-in fetch
#1001 Denver  $89.50
#1002 Tampa   $142.25
fetch polyfilled with node-fetch
#1001 Denver  $89.50
#1002 Tampa   $142.25
Command Prompt running a TypeScript node-fetch polyfill on Node.js 22 with and without --no-experimental-fetch
Same orders either way: the built-in fetch first, then the node-fetch polyfill.

In Jest, put that if block in a setup file and point setupFiles: ["<rootDir>/jest.setup.ts"] at it. Mock fetch per test with jest.spyOn(globalThis, "fetch") when you don’t want real requests.

Server-side rendering hits the same wall: your component’s fetch call runs in the Node version on the server, not in the browser. Check the hosting runtime first, and wrap failed requests as shown in TypeScript exception handling.

ReferenceError vs Cannot find name ‘fetch’ compared

MessageWhenFix
ReferenceError: fetch is not definedRuntime (Node < 18, Jest jsdom, old SSR host)Upgrade Node, import node-fetch/undici, or polyfill
TS2304: Cannot find name ‘fetch’Compile timeAdd DOM to lib or "types": ["node"]
TS2584: Cannot find name ‘console’Compile time, same causeSame lib/types fix

Once fetch works again, these TypeScript tutorials pair well with it:

Frequently asked questions

Why do I get ReferenceError: fetch is not defined in TypeScript?

The JavaScript runs where there’s no global fetch, usually Node.js older than 18 or a Jest jsdom test. TypeScript doesn’t provide fetch at runtime.

Which Node.js version has fetch built in?

Node.js 18.0.0 and later expose a global fetch. It became stable in Node 21.

Does installing @types/node fix the error?

It fixes the compile-time TS2304 error only. If the runtime has no fetch, the program still throws, so upgrade Node or add node-fetch.

Can I use require with node-fetch?

Not with version 3, which is ESM-only. Use import, a dynamic import(), or install node-fetch version 2.

Why does fetch work in the browser but not in my tests?

Jest’s jsdom environment doesn’t implement fetch. Use the node environment or a setup file that polyfills it.

Where is the lib option documented?

See the lib compiler option in the TypeScript TSConfig reference.