Fetch API
MedThe Fetch API provides a modern, Promise-based interface for making HTTP requests. A critical gotcha: fetch only rejects on network failures, NOT on HTTP error status codes like 404 or 500 — you must check response.ok yourself.
Interactive Visualization
DOM Events
document>
body>
div>
button
Phase: Capture
Event travels DOWN from document to target
Key Points
- fetch() returns a Promise<Response> — does NOT reject on HTTP errors (404, 500)
- Always check response.ok or response.status before parsing the body
- Body parsing methods: json(), text(), blob(), arrayBuffer(), formData()
- Request options: method, headers, body, mode, credentials, signal
- AbortController.signal cancels in-flight requests via the signal option
- CORS: cross-origin requests require server-side Access-Control-Allow-Origin headers
Code Examples
Basic GET with Error Handling
async function fetchData(url) { const response = await fetch(url); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } return response.json(); } try { const data = await fetchData('/api/users'); console.log(data); } catch (err) { console.error('Request failed:', err.message); }
The biggest fetch gotcha: it only rejects on network failure. Always check response.ok.
POST, PUT, and DELETE
// POST with JSON body const response = await fetch('/api/users', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: 'Alice' }), }); // DELETE await fetch('/api/users/123', { method: 'DELETE' }); // POST with FormData (file uploads) const form = document.querySelector('form'); await fetch('/api/upload', { method: 'POST', body: new FormData(form), // Content-Type set automatically });
Set method, headers, and body for non-GET requests. FormData handles file uploads automatically.
AbortController Integration
async function fetchWithTimeout(url, options = {}, timeoutMs = 5000) { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), timeoutMs); try { const response = await fetch(url, { ...options, signal: controller.signal, }); if (!response.ok) throw new Error(`HTTP ${response.status}`); return response; } finally { clearTimeout(timeout); } }
AbortController cancels in-flight requests. Pass controller.signal to fetch, call abort() to cancel.
Parallel Requests
async function fetchMultiple(urls) { const responses = await Promise.all( urls.map(url => fetch(url)) ); for (const res of responses) { if (!res.ok) throw new Error(`Failed: ${res.url}`); } return Promise.all(responses.map(r => r.json())); } const [users, posts] = await fetchMultiple([ '/api/users', '/api/posts', ]);
Promise.all fires multiple requests in parallel for better performance.
Common Mistakes
- Assuming fetch rejects on HTTP errors like 404 or 500
- Forgetting Content-Type: application/json header when sending JSON
- Reading the response body twice (body is a stream, consumed once)
- Not handling AbortError separately from other errors
Interview Tips
- Lead with the biggest gotcha: fetch resolves on 404, only rejects on network failure
- Compare fetch to XMLHttpRequest: cleaner API, Promise-based
- AbortController provides request cancellation
- CORS basics: same-origin default, cross-origin needs server headers