Connecting to the World
What I Built
The Dev Bookshelf page — fetches real book data and cover images from the Open Library API. On page load it renders 6 curated favourites. A search bar lets users find any book via a live API query, with debouncing to avoid hammering the server on every keystroke.
What I Learned
async/awaitmakes asynchronous code read like synchronous code — much easier than promise chains once it clicks.- Always wrap
fetch()calls in try/catch. Networks fail. APIs go down. Users need a useful error message, not a blank page. - Debouncing means "wait until the user stops typing before making the API call". A 400ms delay avoids 20 requests for a single search.
- Open Library's cover image URL is just
https://covers.openlibrary.org/b/isbn/{isbn}-M.jpg— no auth required.
Code highlight — async fetch with error handling
async function searchBooks(query) {
const resultsEl = document.getElementById('search-results');
resultsEl.innerHTML = '<p class="loading">Searching...</p>';
try {
const url = `https://openlibrary.org/search.json` +
`?q=${encodeURIComponent(query)}` +
`&fields=title,author_name,cover_i,first_publish_year` +
`&limit=12`;
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
renderBooks(data.docs, resultsEl);
} catch (err) {
resultsEl.innerHTML =
`<p class="error">Could not load results. Try again.</p>`;
}
}
Challenge & Fix
Problem: Some books had no cover image. My <img> tags showed broken image icons.
Fix: Added an onerror handler on each image: if the cover fails to load, swap the src to a CSS-only placeholder with the book title as text. Also added a loading="lazy" attribute to defer off-screen cover loads.
Skills Gained
Fetch API
async/await
try/catch
JSON parsing
Debouncing
Dynamic DOM rendering
REST APIs