The browser-native Promise-based API for making HTTP requests — replacing XMLHttpRequest with a cleaner interface supporting streaming, CORS, and request/response objects.
Explanation
fetch(url, options) returns a Promise that resolves to a Response object as soon as headers arrive — not after the body is fully received. This means a 404 or 500 does not reject the Promise; you must check response.ok or response.status explicitly. The body is a readable stream consumed by methods like response.json(), response.text(), or response.arrayBuffer(), each returning a Promise. The options object controls method, headers, body (string, FormData, Blob, ReadableStream), mode (cors, no-cors, same-origin), credentials (include cookies with 'include'), and signal (AbortController for cancellation). In Node.js, fetch has been native since v18. Common patterns include adding auth headers via a wrapper function, handling errors uniformly, and cancelling in-flight requests when a component unmounts (React) or a new search starts.
Watch Out
⚠ The body of a Response can only be consumed once — calling response.json() and then response.text() on the same response throws 'body already used'. Clone the response with response.clone() if you need to read it twice.
Common Misconception
✗ A non-2xx HTTP response (404, 500) does NOT reject the fetch Promise — the Promise resolves with a Response where response.ok is false. Only network failures (no connection, CORS block) cause rejection.
Why It Matters
fetch is the standard for browser HTTP requests and Node.js 18+ — understanding response.ok, body streaming, and AbortController is essential for any JS frontend or backend HTTP code.
Common Mistakes
Not checking response.ok — a 500 response resolves the Promise, so error handling is silently skipped.
Calling response.json() without awaiting response first — json() returns a Promise that must also be awaited.
Sending JSON without setting Content-Type: application/json — the server receives the body but cannot parse it.
Not aborting fetch on component unmount — causes state updates on unmounted components and memory leaks in SPAs.
Code Examples
💡 Note
The bad example calls res.json() unconditionally — a 404 response resolves the Promise and parses the error body as data. The fix checks response.ok and throws, and accepts an AbortSignal for cancellation.
✗ Vulnerable
// Missing response.ok check — silent failure on 4xx/5xx:
async function getUser(id) {
const res = await fetch(`/api/users/${id}`);
return res.json(); // parses error body as if it were success
}
✓ Fixed
// Check ok, handle errors, support cancellation:
async function getUser(id, signal) {
const res = await fetch(`/api/users/${id}`, {
headers: { Accept: 'application/json' },
signal, // AbortController signal
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
}