Fetch, CORS and browser storage
Await-await-ok, CORS in practice, and cookies versus storages.
fetch resolves on HTTP errors, browsers gate cross-origin reads, and
storage is strings-only — three facts that decide whether data code works.
Interviewers ask this because "await, await, ok" plus a CORS explanation
covers most real data bugs.
Real-life situation
A profile loader awaits the user, then the avatar, then renders — but a 404
renders as a broken page instead of "user missing", because fetch never
rejected. Checking res.ok turns the silent bad render into a handled
empty state. Meanwhile a search box aborts its previous request on every
keystroke, so slow replies can't overwrite fresh ones.
Visual
Try it
async function loadUser(id) {
const res = await fakeFetch("/users/" + id, { latencyMs: 5 });
if (!res.ok) {
return "user missing:" + res.status;
}
const user = await res.json();
return user.name;
}
console.log(await loadUser(1));
console.log(await loadUser(99));
async function loadAbort() {
const controller = new AbortController();
const pending = fakeFetch("/users", {
latencyMs: 50,
signal: controller.signal,
});
controller.abort();
try {
await pending;
return "arrived";
} catch (error) {
return error.name;
}
}
console.log(await loadAbort());Expected output
Aarav Sharma user missing:404 AbortError
Try pointing the first call at /flaky and watch it throw instead of
returning. Then give loadAbort no abort and confirm "arrived".
function saveDraft(text) {
return JSON.stringify({ text: text });
}
function loadDraft(raw) {
try {
return JSON.parse(raw).text;
} catch (error) {
return "empty";
}
}
const raw = saveDraft("hi");
console.log(loadDraft(raw));
console.log(loadDraft("###"));
console.log(loadDraft(null));Expected output
hi empty empty
Runner helpers
sleep(ms) waits; fakeFetch(url) returns deterministic sample data. Console code runs in a worker with a 5-second limit.
Cookies versus storages
Cookies travel with every request (sessions, auth flags) and hide behind
HttpOnly. localStorage persists per origin without traveling (themes,
drafts). sessionStorage dies with the tab (wizard state). Secrets go in
HttpOnly cookies, never in readable storage — one paragraph on XSS: build
markup only from trusted strings, or attacker text becomes your code.
Fetch result shape — guess first, then reveal.
async function status(id) {
const res = await fakeFetch("/users/" + id, { latencyMs: 5 });
return res.ok ? "ok" : "fail:" + res.status;
}
status(2).then(function (s) {
console.log(s);
});Interview questions
What does fetch return and what must you check yourself?basic
A promise of a Response — await it, then await the body (res.json()), then check res.ok, because fetch resolves on HTTP ERRORS (404, 500) and rejects only on network failure. Three steps, three classic bugs: one await (body never read), no ok check (errors treated as data), no catch (offline crashes). Recite "await, await, ok" and you will never ship the broken version.
Interviewers also ask
- What are the three classic fetch bugs?
How do GET, POST, PUT, PATCH and DELETE differ in practice?intermediate
GET reads (cacheable, no body); POST creates or triggers actions; PUT replaces a whole resource; PATCH partially updates it; DELETE removes. Browsers and caches treat GET as safe to repeat and retry — never mutate through GET, or refreshes and prefetchers will fire your writes twice. Match the verb to the intent and say why; REST literacy is what is being tested, not memorized definitions.
Interviewers also ask
- Why must GET never mutate?
What is CORS in one practical paragraph?intermediate
Browsers block pages from reading responses of OTHER origins unless that server opts in with allow-origin headers. Your fetch still SENDS (so mutations can happen), but your code can't READ the reply — the classic "works in curl, fails in the browser". Fixes live server-side (headers, a same-origin proxy); browser-side workarounds are hacks. Say "server problem, not a fetch bug" first.
Interviewers also ask
- Why does the same request work in curl?
Cookies vs localStorage vs sessionStorage: when do you use each?basic
Cookies: tiny (~4KB), sent with EVERY request automatically — sessions and auth tokens, with HttpOnly/Secure/SameSite flags doing the real security work. localStorage: ~5MB, never sent, survives restarts — themes, drafts, caches. sessionStorage: same API, dies with the tab — per-tab wizard state. Never store secrets where JS can read them if a cookie flag could hide them instead.
Interviewers also ask
- Where should an auth token live?
What is XSS in one paragraph, and what is the one rule that stops it?intermediate
Cross-site scripting: attacker text reaching the page as CODE — usually via innerHTML with user strings — running with your site's privileges (cookie theft, action forgery). The one rule: never build markup from untrusted strings; use textContent and let the framework escape. Second layer: HttpOnly cookies (stolen-session damage control) and a Content Security Policy. Interviews accept "textContent, HttpOnly, CSP" as the complete answer.
Interviewers also ask
- What does HttpOnly actually protect?
How do you store an object in localStorage?basic
localStorage holds STRINGS only, so JSON.stringify on the way in and JSON.parse on the way out, wrapped in try/catch (private mode and quota errors throw). Read with a fallback default for first visits, and version your keys (cart:v1) so schema changes don't resurrect ancient shapes. Anything sensitive or large belongs server-side, not in the browser store.
Interviewers also ask
- What breaks when the stored schema changes?
How do you keep two tabs' localStorage views in sync?intermediate
Listen for the storage event: it fires in every OTHER tab when one tab writes, carrying the key and new value — re-read and re-render on it. It does NOT fire in the writing tab itself, and it never fires for sessionStorage or cookies. For same-tab reactivity, wrap your setter to notify locally too. Name the event and its blind spots; that is the whole interview answer.
Interviewers also ask
- Does the writing tab get the event?
Key takeaways
fetchneeds two awaits plus anokcheck; it rejects only on network failure.- CORS failures are server configuration, not fetch bugs.
- Cookies travel, storages stay, sessions die with the tab.
References
Last verified: 2026-10-12.
Runner helpers
sleep(ms)waits;fakeFetch(url)returns deterministic sample data. Console code runs in a worker with a 5-second limit.