Topic 11 / 15
fetch & Working with APIs
GET — Reading Data
async function getUsers() {
const response = await fetch("https://api.example.com/users");
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}
const users = await getUsers();The two-step is always the same: check response.ok, then parse the body. Remember: fetch only rejects on network failure — a 404 or 500 is a “successful” fetch with ok: false.
Query Parameters — Use URLSearchParams
const params = new URLSearchParams({
q: "django tutorial",
page: 2,
});
const response = await fetch(`/api/search?${params}`);
// /api/search?q=django+tutorial&page=2 — encoding handled for youPOST — Sending JSON
async function createCourse(course) {
const response = await fetch("/api/courses", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`,
},
body: JSON.stringify(course),
});
if (!response.ok) {
const problem = await response.json().catch(() => ({}));
throw new Error(problem.message ?? `HTTP ${response.status}`);
}
return response.json();
}
await createCourse({ title: "JS Mastery", price: 199 });Other methods follow the same shape: PUT/PATCH to update, DELETE to remove.
A Reusable API Helper
Real apps wrap fetch once instead of repeating boilerplate:
async function api(path, { method = "GET", body, ...options } = {}) {
const response = await fetch(`https://api.example.com${path}`, {
method,
headers: {
"Content-Type": "application/json",
...options.headers,
},
body: body ? JSON.stringify(body) : undefined,
});
if (!response.ok) throw new Error(`HTTP ${response.status} on ${path}`);
return response.status === 204 ? null : response.json();
}
// usage reads beautifully:
const users = await api("/users");
await api("/courses", { method: "POST", body: { title: "CSS" } });The Full UI Pattern: Loading / Error / Data
async function showCourses() {
spinner.hidden = false;
errorBox.hidden = true;
try {
const courses = await api("/courses");
renderList(courses);
} catch (err) {
errorBox.textContent = "Couldn't load courses. Retry?";
errorBox.hidden = false;
} finally {
spinner.hidden = true;
}
}Every data-driven screen you’ll ever build — vanilla or React — is a variation of these three states.
Aborting Requests
const controller = new AbortController();
fetch("/api/big-search", { signal: controller.signal });
controller.abort(); // e.g. when the user types a new query