The native fetch() API is the standard way to make HTTP requests in modern JavaScript. However, one common trap for developers is assuming fetch() automatically rejects a promise when a server returns an HTTP error code (like 404 Not Found or 500 Internal Server Error).
In this guide, we will look at how fetch() handles errors and how to write a reliable wrapper for clean API requests.
The Misconception with fetch()
Unlike libraries like Axios, fetch() only rejects a Promise if there is a network failure or if the request was blocked (e.g., DNS lookup failure or CORS issues). An HTTP status code of 404 or 500 is still considered a successful HTTP response.
This is an illustration below:
JavaScript code:
// ❌ Incorrect Error Handling
async function getUser() {
try {
const response = await fetch('/api/user/999');
// Returns 404
const data = await response.json();
// Still runs, potentially parsing invalid JSON
console.log(data);
} catch (error) {
// This will NOT run on 404 or 500 status codes!
console.error('Network Error:', error);
}
}
The Correct Approach is to Check response.ok
The response object contains a boolean property called ok, which is true if the HTTP status code is in the range 200–299. You should explicitly check this flag before parsing JSON.
**JavaScript code:
// ✅ Correct Error Handling
async function getUser(userId) {
try {
const response = await fetch(/api/user/${userId});
// Explicitly check for HTTP errors
if (!response.ok) {
throw new Error(`HTTP Error! Status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error('Fetch operation failed:', error.message);
}
}
-->Creating a Reusable Fetch Helper:
To keep your frontend codebase clean, wrap this check into a utility function for all your application's API calls:
JavaScript code:
async function request(url, options = {}) {
const response = await fetch(url, options);
if (!response.ok) {
const errorBody = await response.text();
throw new Error([${response.status}] ${response.statusText}: ${errorBody});
}
return response.json();
}
// Example usage
async function loadData() {
try {
const data = await request('https://jsonplaceholder.typicode.com/posts/1');
console.log('Data loaded:', data);
} catch (err) {
console.error('API Error:', err.message);
}
}
Conclusion:
Checking response.ok ensures your asynchronous logic fails early and gracefully when an endpoint returns an error state.
"Need developer documentation, technical breakdowns, or API guides written for your SaaS app? I write technical content for software builders and dev tools. Connect with me or send a DM!"
Top comments (0)