← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

Fetch API

JavaScript ES2015 Beginner
debt(d7/e1/b3/t9)
d7 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'only careful code review or runtime testing' (d7). The detection_hints list eslint and semgrep as tools, with a pattern matching 'await fetch( not followed by response.ok check' — this is a specialist/configured lint rule, not a default out-of-the-box catch. Most developers won't have this rule enabled by default, and the bug silently passes in production when servers return 4xx/5xx, so it sits closer to d7 than d5.

e1 Effort Remediation debt — work required to fix once spotted

Closest to 'one-line patch or single-call swap' (e1). The quick_fix is explicit: add a single `if (!res.ok) throw new Error(...)` line immediately after each `await fetch()`. This is a minimal, surgical fix per call site.

b3 Burden Structural debt — long-term weight of choosing wrong

Closest to 'localised tax' (b3). The fetch pattern is used wherever HTTP calls are made, but each call site is independently fixable. It doesn't impose a cross-cutting architectural obligation; it's a localised discipline required per usage site rather than a system-wide structural choice.

t9 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'catastrophic trap — the obvious way is always wrong' (t9). The misconception is canonical and severe: every developer reasonably expects a Promise to reject on HTTP errors (as axios does), but fetch resolves on 4xx/5xx. The 'obvious' error handling pattern — try/catch around await fetch — silently fails to catch server errors. This directly matches the t9 anchor: the intuitive approach is always wrong for error handling.

About DEBT scoring →

Also Known As

window.fetch Fetch API HTTP fetch browser fetch

TL;DR

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();
}

Added 10 Apr 2026
Views 50
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 1 ping S 0 pings M 0 pings T 0 pings W 1 ping T 0 pings F 1 ping S 1 ping S 2 pings M 0 pings T 2 pings W 0 pings T 1 ping F 0 pings S 0 pings S 0 pings M 1 ping T 0 pings W 0 pings T 1 ping F 0 pings S 1 ping S 0 pings M 0 pings T 0 pings W
No pings yet today
No pings yesterday
Google 9 ChatGPT 3 Perplexity 3 Ahrefs 3 Bing 3 Scrapy 3 Claude 2 Meta AI 2 Unknown AI 1 Qwen 1 Majestic 1 SEMrush 1
crawler 26 crawler_json 6
DEV INTEL Tools & Severity
🟡 Medium ⚙ Fix effort: Low
⚡ Quick Fix
Add if (!res.ok) throw new Error(`HTTP ${res.status}`) immediately after every await fetch()
🔗 Prerequisites
🔍 Detection Hints
await fetch( not followed by response.ok or response.status check
Auto-detectable: ✓ Yes eslint semgrep
⚠ Related Problems
🤖 AI Agent
Confidence: Medium False Positives: Medium ✗ Manual fix Fix: Low Context: Function Tests: Update
CWE-390 CWE-755


✓ schema.org compliant