A resolved fetch promise does not mean the server returned a successful HTTP status. A 404 or 500 response can still resolve normally, while a network failure may reject. Separating transport errors, HTTP failures and unexpected data makes both the interface and debugging more reliable.
Use response.ok to test for a status in the successful 200–299 range. Decide what your application expects from that endpoint: a successful response with no content is different from a successful JSON object. Do not call response.json on every response without considering its contract.
async function loadCourses(fetchImpl = fetch) {
const response = await fetchImpl("/api/courses");
if (!response.ok) {
throw new Error("Request failed: HTTP " + response.status);
}
const data = await response.json();
if (!data || !Array.isArray(data.courses)) {
throw new Error("Unexpected courses response");
}
return data.courses;
}
// A deterministic success case without a network request
const fakeFetch = async () => new Response(
JSON.stringify({ courses: [] }),
{ status: 200, headers: { "Content-Type": "application/json" } }
);
loadCourses(fakeFetch).then(rows => console.assert(rows.length === 0));JSON parsing checks whether the body is valid JSON. The shape check verifies that the application received a courses array. A valid JSON error object, an HTML login page or an empty body can all violate the endpoint contract in different ways. Keep enough diagnostic information to tell these failures apart.
The interface should distinguish loading, empty results and failure. An empty course array is a successful response with no items, not an error. On failure, offer a clear retry action when appropriate. Avoid displaying raw server stack traces or an entire response body to users.
Retries should be bounded and based on the operation. Repeating a read after a transient failure is different from repeating a payment or creation request. For mutations, understand idempotency before retrying. CORS failures also require fixing the request or server policy, not repeatedly issuing the same request.
Replace fakeFetch with mocks for HTTP 404, invalid JSON and a JSON object with no courses array. Verify that each fails and that an empty array still succeeds. Keep all four cases in a small regression test.
Explore the related Code Hacker course syllabus for structured learning. Then read Prevent Stale Search Results with AbortController to connect this exercise to the next skill.
MDN Using Fetch provides the underlying API or technical guidance. Check the documentation for the version you use; examples here are small learning exercises rather than complete production implementations.
Found an issue with this guide? Send a correction with the article URL and your example. About our learning material.
Subscribe to Code Hacker and get hands-on access to 40+ premium courses — Ethical Hacking, Kali Linux, Metasploit, Network Hacking, Bug Bounty & more!