Fetch is deceptively simple: one call, one promise, done. Production networking needs more — requests that hang forever, fail halfway, or resolve after the user has moved on. Here is the small wrapper I reuse on every project.
Timeouts and retries
Fetch has no built-in timeout, so add one with an AbortController: race the request against a timer, and abort anything slower than ten seconds. Retry idempotent GETs with exponential backoff, but never blindly retry POSTs — a payment submitted twice is worse than one that failed loudly.
Cancel what the user abandoned
Tie every request to the component or interaction that started it. When a user types a new search character, abort the previous query; when they leave the page, abort the lot. Stale responses updating fresh UI is a whole category of bug you can delete at the source.
- Always handle non-200 statuses — fetch only rejects on network failure.
- Show skeletons while loading, and specific recovery actions on error.
- Log failures with context: endpoint, timing and retry count.
Networks fail; that's their nature. A fetching layer that expects failure turns outages into polite messages and retries instead of blank screens.
Mateo Alvarado
Web developer & UI/UX designer, Semicolon Studio