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:
fetchadded, but only behind the--experimental-fetchflag - v18.0.0: global
fetchon 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

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'.

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

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

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
| Message | When | Fix |
|---|---|---|
| ReferenceError: fetch is not defined | Runtime (Node < 18, Jest jsdom, old SSR host) | Upgrade Node, import node-fetch/undici, or polyfill |
| TS2304: Cannot find name ‘fetch’ | Compile time | Add DOM to lib or "types": ["node"] |
| TS2584: Cannot find name ‘console’ | Compile time, same cause | Same lib/types fix |
Once fetch works again, these TypeScript tutorials pair well with it:
- Convert a string to a number in TypeScript
- TypeScript satisfies vs as
- The full TypeScript keywords list
- Type an array of objects with an interface
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.