22 minLesson 14 of 40
JavaScript for Web
Fetch API & Async JavaScript
Fetch API & Async JavaScript
Modern web apps load data from APIs without page reloads. The Fetch API combined with async/await is the standard way to do this — cleaner than callbacks, more readable than raw Promises.
Basic Fetch
// fetch returns a Promise that resolves to a Response
const response = await fetch("https://api.example.com/users");
const users = await response.json(); // parse JSON body
// Always check response.ok — 404/500 don't reject the Promise!
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
CRUD Operations
// GET
const user = await fetch("/api/users/42").then(r => r.json());
// POST — send JSON
const newUser = await fetch("/api/users", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "Alice", email: "alice@example.com" })
}).then(r => r.json());
// PUT — update
await fetch(`/api/users/42`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "Alice B" })
});
// DELETE
await fetch(`/api/users/42`, { method: "DELETE" });
Error Handling Pattern
async function apiFetch(url, options = {}) {
const response = await fetch(url, {
headers: { "Content-Type": "application/json" },
...options
});
if (!response.ok) {
const err = await response.json().catch(() => ({ message: response.statusText }));
throw Object.assign(new Error(err.message), { status: response.status });
}
if (response.status === 204) return null;
return response.json();
}
// Usage
try {
const users = await apiFetch("/api/users");
renderUsers(users);
} catch (err) {
if (err.status === 401) redirectToLogin();
else showErrorBanner(err.message);
}
Loading States Pattern
async function loadData(url, containerEl) {
// Show loading
containerEl.innerHTML = `<div class="spinner"></div>`;
containerEl.setAttribute("aria-busy", "true");
try {
const data = await apiFetch(url);
containerEl.innerHTML = renderContent(data);
} catch (err) {
containerEl.innerHTML = `
<div class="error">
<p>${err.message}</p>
<button onclick="loadData('${url}', this.closest('.container'))">
Try Again
</button>
</div>
`;
} finally {
containerEl.removeAttribute("aria-busy");
}
}
Parallel Requests
// Sequential — slow (waits for each one)
const user = await apiFetch(`/api/users/${id}`);
const posts = await apiFetch(`/api/users/${id}/posts`);
const followers = await apiFetch(`/api/users/${id}/followers`);
// Total time = time1 + time2 + time3
// Parallel — fast (all at once)
const [user, posts, followers] = await Promise.all([
apiFetch(`/api/users/${id}`),
apiFetch(`/api/users/${id}/posts`),
apiFetch(`/api/users/${id}/followers`)
]);
// Total time = max(time1, time2, time3)
Cancelling Requests (AbortController)
// Cancel previous request when user types (search input)
let searchController = null;
async function search(query) {
// Cancel any ongoing search
searchController?.abort();
searchController = new AbortController();
try {
const results = await apiFetch(
`/api/search?q=${encodeURIComponent(query)}`,
{ signal: searchController.signal }
);
renderResults(results);
} catch (err) {
if (err.name === "AbortError") return; // expected, ignore
showError(err.message);
}
}
searchInput.addEventListener("input", debounce(e => search(e.target.value), 300));
Real Example: Comment Section
async function initComments(postId) {
const container = document.querySelector(".comments");
const form = document.querySelector("#comment-form");
// Load comments
await loadData(`/api/posts/${postId}/comments`, container);
// Add comment
form.addEventListener("submit", async (e) => {
e.preventDefault();
const text = form.querySelector("textarea").value.trim();
if (!text) return;
const submitBtn = form.querySelector("[type='submit']");
submitBtn.disabled = true;
submitBtn.textContent = "Posting...";
try {
const comment = await apiFetch(`/api/posts/${postId}/comments`, {
method: "POST",
body: JSON.stringify({ text })
});
form.reset();
// Prepend new comment to list
container.insertAdjacentHTML("afterbegin", renderComment(comment));
} catch (err) {
alert("Failed to post comment: " + err.message);
} finally {
submitBtn.disabled = false;
submitBtn.textContent = "Post Comment";
}
});
}
Next lesson: Why React? JSX & Component Thinking — the foundation of modern UI development.
📱
Get Notes Free →Get this course's notes on Telegram!
Free cheat sheets, summaries & practice exercises