Fetch and Error Handling
Request data with the Fetch API and handle both success and failure paths.
What you'll learn
- Explain what the fetch function does and what it returns
- Handle a fetch response's JSON body with await
- Use try/catch to handle a failed request without crashing the program
Prerequisites
Explanation
fetch is the browser's built-in function for making HTTP requests — the same request/response mechanics from the foundations track, but triggered by your own code instead of typing a URL. It returns a Promise that resolves to a Response object once the server replies:
const response = await fetch("/api/books");
const data = await response.json(); // parses the JSON body
Note the two awaits: the first waits for the response to arrive, the second waits for its body to be read and parsed as JSON (reading a body is itself asynchronous).
Because network requests can fail in many ways — no connection, a slow server, a bad URL, a server-side error — production code always needs a plan for failure. try/catch is how JavaScript handles that:
async function loadBooks() {
try {
const response = await fetch("/api/books");
if (!response.ok) {
throw new Error("Server responded with status " + response.status);
}
const data = await response.json();
return data;
} catch (error) {
console.error("Failed to load books:", error.message);
return [];
}
}
Two details matter here. First, fetch only rejects (triggers catch) for network-level failures — a 404 or 500 response is still a "successful" fetch from JavaScript's point of view, which is why you must check response.ok (true for 2xx statuses) yourself and throw explicitly if it's false. Second, an unhandled rejected Promise (an async error nobody catches) is a real production bug — it can silently break a feature or spam error logs, so always wrap awaited calls that can fail in try/catch, and give the user a clear fallback state instead of a frozen or blank UI.
This sandbox has no real network access, so exercises here use a small mock fetch-like function that behaves the same way (returning a Promise, sometimes rejecting) so you can practice the exact same handling patterns you'll use with a real API.
Example
A mock fetch-like function handled with async/await and try/catch.
function mockFetchUser(id) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (id === 1) {
resolve({ id: 1, name: "Ada" });
} else {
reject(new Error("User not found"));
}
}, 10);
});
}
async function loadUser(id) {
try {
const user = await mockFetchUser(id);
console.log("Loaded:", user.name);
} catch (error) {
console.error("Could not load user:", error.message);
}
}
loadUser(1);
loadUser(99);Try it yourself
Try calling loadUser with a different id and see the error path run.
Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.
Guided exercise
Guided exercise
Using the provided mockFetchNumber(shouldFail) helper, write an async function `safeDouble(shouldFail)` that returns the doubled resolved number on success, or the string 'error' if the call rejects.
Checks: Success path doubles the resolved number · Failure path returns 'error' instead of rejecting
Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.
Stuck? Get a hint.
Independent exercise
Independent exercise
Using the provided mockFetchBook(id) helper (rejects for id !== 1), write an async function `loadBookTitle(id)` that returns the book's title on success, or the string 'Book not found' on failure — without ever throwing an uncaught error.
Checks: Existing book resolves with its title · plus 1 hidden check
Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.
Stuck? Get a hint.
Common mistakes
- Not checking response.ok, so a 404 or 500 response is treated as if it succeeded.
- Leaving an awaited call outside of any try/catch, letting a real network failure crash the whole feature.
- Showing a blank or frozen UI on error instead of a clear, honest message to the user.
Knowledge check
Takeaway
fetch gets you data over the network; try/catch and response.ok make sure failure has a real plan, not a crash.
Summary
fetch returns a Promise resolving to a Response; awaiting response.json() parses its body. Because fetch only rejects on network failure, you must check response.ok yourself, and try/catch keeps both network errors and bad statuses from crashing your program.
References
Your notes
Notes save automatically.
Finished this lesson?
Mark it complete to track your progress and schedule a future review.
AI tutor
The optional AI tutor isn't enabled in this deployment. All lessons, exercises, quizzes, and search work fully without it.