Master the Fetch API for making HTTP requests in JavaScript, including GET, POST, error handling, and working with JSON responses.
fetch() to make HTTP requests and await the response.response.ok, since fetch rejects only on network failure.try/catch for clean, readable fetch code.fetch, since it supports cache and revalidate options for data fetching.AbortController to cancel in-flight requests on unmount, which prevents wasted network calls and stale state updates.response.json() on an error response that returns HTML, like a 404 page, throws a JSON parse error.res.text() as a safe fallback when the content type of a response is unknown or unconfirmed.Content-Type header on a POST request can cause the server to misparse the JSON body.fetch()Returns a Promise that resolves to a Response object, the entry point for every request.
async function getUser(id) {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
return response.json();
}response.okTrue for status codes 200-299 — always check it before trusting the response.
response.json()Parses the response body as JSON and returns another Promise.
const data = await response.json();Network-only rejectionfetch only rejects on network-level failures; a 404 or 500 still resolves successfully.
Replaces XMLHttpRequestFetch is the modern, promise-based replacement for the older XMLHttpRequest API.
Sending JSONSet method, headers, and body in the options object to send data.
async function createPost(data) {
const response = await fetch('/api/posts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
if (!response.ok) throw new Error('Request failed');
return response.json();
}methodSets the HTTP verb, like 'POST', 'PUT', or 'DELETE'.
headersSets request headers, most commonly Content-Type for JSON bodies.
headers: { 'Content-Type': 'application/json' }bodyHolds the request payload, usually JSON.stringify(data) for JSON APIs.
body: JSON.stringify(data)credentialsControls whether cookies are sent, e.g. 'include' or 'same-origin'.
Robust try/catchHandle both network errors and HTTP error responses in one function.
async function safeFetch(url, options = {}) {
try {
const res = await fetch(url, options);
if (!res.ok) {
const msg = await res.text();
throw new Error(`${res.status}: ${msg}`);
}
return await res.json();
} catch (err) {
console.error('Fetch failed:', err);
throw err;
}
}Network-level errorsWrap fetch in try/catch to catch network-level errors like being offline or CORS failures.
HTTP error statusCheck response.ok inside the try block to catch HTTP errors (4xx, 5xx).
Reading the error bodyRead res.text() or res.json() on error responses to get the server's error message.
const msg = await res.text();Auth tokenSend a bearer token in the Authorization header.
headers: { Authorization: 'Bearer ' + token }Form dataSend a FormData body directly — no Content-Type header needed.
body: new FormData(formEl)Abort a requestCancel an in-flight request with AbortController.
const ac = new AbortController();
fetch(url, { signal: ac.signal });Read plain textRead a non-JSON response body as text.
const text = await response.text();Download a blobRead a binary response body as a Blob.
const blob = await response.blob();fetch()Best for simple requests with no extra dependencies; error handling is more verbose and there are no interceptors.
axiosBest for complex apps needing interceptors and retries; adds an external dependency of about 15 kB gzipped.
SWR / React QueryBest for data fetching in React with built-in caching; framework-specific and needs more setup.
tRPCBest for full-stack TypeScript with end-to-end types; requires a matching server setup.
fetch() only rejects its promise on network-level failures (e.g., no internet, DNS error). HTTP error status codes like 404 or 500 resolve successfully. You must check response.ok or response.status manually before treating the response as valid.
Set the method to 'POST', add a 'Content-Type': 'application/json' header, and pass JSON.stringify(yourData) as the body. Without the Content-Type header, many servers won't parse the payload correctly.
fetch() is built into modern browsers and Node.js 18+, making it a solid zero-dependency choice for most projects. Axios adds value if you need request/response interceptors, automatic JSON serialization, or broader legacy browser support out of the box.
Wrap your fetch call in a try/catch to handle network failures. Inside the try block, explicitly check response.ok and throw a new Error for HTTP errors. This lets your catch block handle both failure types in one place.
Yes — Next.js App Router extends the native fetch with cache and next.revalidate options. This lets you control caching and incremental static regeneration directly in server components without any additional library.
Fetching with Timeout and Graceful Cancellation
Combines AbortController, try/catch, and a response.ok check to fetch data that gives up after a timeout instead of hanging forever.
async function fetchWithTimeout(url, timeoutMs = 5000) {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeoutMs);
try {
const response = await fetch(url, { signal: controller.signal });
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
return await response.json();
} catch (error) {
if (error.name === 'AbortError') {
throw new Error('Request timed out');
}
throw error;
} finally {
clearTimeout(timer);
}
}
fetchWithTimeout('/api/users').then(data => console.log(data));AbortController turns a fetch call into a cancellable operation — pair it with a timer to avoid requests that hang forever.