Request and Response Basics
Request and Response Basics
fetch(url) returns a Promise resolving to a Response — headers and body stream, not parsed JSON. Defaults: GET, credentials: 'same-origin', and it does not reject on HTTP errors; a 404 or 500 resolves with response.ok === false.
const res = await fetch('/api/items');
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const items = await res.json();
The body can be consumed once — calling res.json() twice throws "body already used." Methods that send data set headers explicitly: Content-Type: application/json with JSON.stringify(body). fetch rejects only on network failure (offline, DNS, CORS block), which is a different branch from HTTP status handling.
Aborting and Retrying
Aborting and Retrying
A request you no longer need — user navigated away, a newer keystroke superseded it — should be aborted:
const ctrl = new AbortController();
search(url, { signal: ctrl.signal });
// on next keystroke:
ctrl.abort();
try { ... } catch (e) {
if (e.name === 'AbortError') return; // expected, not a failure
}
A timeout is the same primitive: setTimeout(() => ctrl.abort(), 5000). For retries, wrap in a helper that catches network errors, backs off with jitter, and only retries idempotent methods — retrying a POST can double-charge. Keep one controller per logical request; sharing a controller across parallel fetches aborts them all.
Pitfalls and CORS
Pitfalls and CORS
CORS failures surface as opaque network errors — check the .ok-independent rejection and the browser console for the preflight. Non-simple requests (custom headers, Content-Type: application/json) trigger an OPTIONS preflight; the server must answer it with the right Access-Control-Allow-* headers or the real request never runs.
Cookies cross-origin require credentials: 'include' and Access-Control-Allow-Credentials: true with an explicit origin (not *). Default redirect: 'follow' can mask a login page returned as 200 — inspect res.url or set redirect: 'manual' when that matters.
Interview: state that fetch rejects only on network errors, that you check res.ok for HTTP errors, and that one-shot bodies plus AbortController are the two mechanics most code gets wrong.
Quiz
1. What does fetch do when the server returns 500?
2. Why does calling res.json() a second time fail?
3. What triggers a CORS preflight OPTIONS request?
Flashcards
Question
fetch error semantics
Click to reveal answer
Answer
Rejects on network/CORS failure only; HTTP 4xx/5xx resolve with ok=false.
Question
response.ok
Click to reveal answer
Answer
true for status 200–299 — the check every caller must perform.
Question
AbortController
Click to reveal answer
Answer
signal aborts in-flight fetches; abort surfaces as AbortError (expected).
Question
One-shot body
Click to reveal answer
Answer
res.json()/text() can be called once — clone() if you need both.
Question
Credentialed CORS
Click to reveal answer
Answer
credentials: "include" + Allow-Credentials: true + explicit origin (never *).
Revision Notes
Key Takeaways
- 1. Network errors reject; HTTP errors resolve — branch on both
- 2. Abort superseded requests; back off retries for idempotent methods only
- 3. Preflight and credentials rules explain most "works locally, fails in prod" CORS bugs
Interview Tips
- • Write the error-handling shape out loud: try/catch for network, res.ok check for status, AbortError ignored
- • Explain the preflight sequence when an interviewer mentions custom headers failing cross-origin
Cheat Sheet
fetch resolves 4xx/5xx → check res.ok; rejects only on network/CORS. Body readable once. AbortController for cancel/timeout; retry idempotent only. JSON body → preflight; cookies need credentials.