Introduction & Learning Outcomes
This expanded module deepens your JavaScript knowledge and teaches you how to use web APIs to build interactive, data-driven applications. We focus on asynchronous programming, modern ES6+ features, consuming REST APIs, handling JSON, and building a practical Weather Dashboard that works offline using local sample data. By the end you will be comfortable architecting small API-driven front-end apps and handling common pitfalls like network errors and CORS.
Outcomes
- Explain the JavaScript event loop, promises, and async/await.
- Use fetch() to consume APIs and parse JSON safely.
- Securely handle API keys and understand CORS limitations.
- Build a Weather Dashboard that fetches data and renders it in the DOM.
- Implement loading states, caching, and basic retry logic.
Key Terms & Definitions
- API
- Application Programming Interface — a contract that allows software to interact with another software component.
- Endpoint
- A specific URL where an API exposes data or functionality.
- REST
- Representational State Transfer — architectural style for designing networked applications using HTTP verbs.
- JSON
- JavaScript Object Notation — lightweight data-interchange format, commonly used by APIs.
- Promise
- An object representing the eventual completion or failure of an asynchronous operation.
- async/await
- Syntactic sugar over promises that allows writing asynchronous code in a synchronous style.
- CORS
- Cross-Origin Resource Sharing — browser policy for allowing or blocking cross-origin requests.
- Rate limit
- Limit set by APIs on how many requests can be made in a given time window.
- Fetch
- Browser API to make network requests returning a promise.
- HTTP methods
- GET, POST, PUT, DELETE — verbs used to perform CRUD operations over HTTP.
- Header
- Metadata sent with requests and responses (e.g., Authorization, Content-Type).
- Timeout
- Limit after which a network request is considered failed.
- Cache
- Storing data temporarily to speed up future requests.
- Debounce
- A technique to delay execution to prevent excessive requests (useful for search APIs).
- Throttling
- Limiting how often a function can be called over time.
Detailed Theory (Advanced JavaScript Concepts)
ES6+ Syntax and Useful Patterns
Modern JavaScript (ES6 and later) introduces useful syntax and patterns that make code concise and expressive. Key features include:
- Arrow functions: Shorter function syntax:
const sum = (a,b) => a + b; - Template literals: Easier string interpolation:
`Hello ${name}` - Destructuring: Extract values:
const {temp,humidity} = data; - Spread/rest: Merge arrays/objects or accept variable args:
[...arr],function(...args) - Modules:
exportandimporthelp organize code into reusable files.
The Event Loop, Callbacks & Promises
JavaScript is single-threaded; the event loop manages asynchronous operations. Callbacks were the earliest pattern; promises provide clearer handling:
// Promise example
new Promise((resolve, reject) => {
setTimeout(() => resolve('done'), 1000);
}).then(value => console.log(value)).catch(err => console.error(err));
Promises have states: pending, fulfilled, rejected. Chain them with .then() and handle errors with .catch() or final cleanup with .finally().
async/await
Async functions return promises and allow using await to pause until a promise resolves. Use try/catch to handle errors:
async function loadData(){
try{
const res = await fetch('data.json');
const json = await res.json();
return json;
}catch(err){ console.error('Fetch failed', err); throw err; }
}
fetch() & handling responses
fetch() starts a request and returns a promise resolving to a Response object. Always check response.ok and parse JSON with response.json():
const r = await fetch('/api/items');
if(!r.ok) throw new Error('Network response was not ok');
const data = await r.json();
Error handling, timeouts & retries
Network requests can fail. Implement timeouts and retry strategies with exponential backoff to handle transient failures. Example minimal timeout wrapper:
function fetchWithTimeout(url, opts={}, timeout=7000){
return Promise.race([
fetch(url, opts),
new Promise((_, rej) => setTimeout(() => rej(new Error('Timeout')), timeout))
]);
}
CORS & API keys
Browsers block cross-origin requests unless the server allows them via CORS headers. Never store secret API keys in client-side code for public apps; use a small backend proxy or serverless function to keep keys secret. For offline demos, use sample local JSON files to avoid needing real API keys.
Project — Weather Dashboard (Chosen demo)
The Weather Dashboard demonstrates fetching API data, parsing JSON, rendering to the DOM, error handling, caching, and saving user preferences. Because this course is offline-first, the dashboard will work with a local sample JSON file (sample-weather.json) and also show how to swap in a live API like OpenWeatherMap when a key is available.
What the dashboard teaches
- Building a small UI: search by city, show current conditions, temperature conversion (Celsius/Fahrenheit).
- Using fetch() and async/await to get data.
- Displaying loading states and error messages.
- Caching last successful result in localStorage.
- Graceful degradation when offline.
Sample Data (sample-weather.json)
{
"city": "Harare",
"country": "ZW",
"temp_c": 22.3,
"condition": "Partly cloudy",
"humidity": 60,
"wind_kph": 12.3,
"forecast": [
{"day":"Today","high_c":25,"low_c":16},
{"day":"Tomorrow","high_c":27,"low_c":17}
]
}
Live Playground — Weather Dashboard (Editable)
Edit the bundled HTML/JS on the left and press Run to preview. The preview fetches sample-weather.json embedded in the document (no network required).
Practical Labs — Step-by-step
Lab 1 — Simple API fetch and display
- Use the playground: press "Use Sample Data" and inspect the DOM update.
- Replace the sample fetch function with a real fetch to
data.jsonwhen you host the JSON locally.
Lab 2 — Convert temperatures and persist preferences
- Add a toggle to switch between °C and °F. Save the preference to
localStorage. - On load, read preference and render accordingly.
Lab 3 — Implement retry logic and timeout
- Create
fetchWithTimeoutwrapper and implement exponential backoff for retries. - Limit retries to a reasonable number (e.g., 3) and show informative user messages.
Lab 4 — Debounce search input
- Implement a debounce function so that typing a city name does not trigger too many searches.
- Use the debounce on
inputevents and trigger the fetch only after user stops typing for 400ms.
Advanced Topics & Best Practices
Handling API keys securely
Never embed secret API keys in client-side code. Use a small server (Node/Express) or serverless function to store keys and forward requests. This also allows you to enforce rate limits and caching. For example, create an endpoint /api/weather?city=Harare that calls the external API with the secret key and returns sanitized JSON to the browser.
Pagination and large datasets
When APIs return large datasets, use pagination (page & limit) or cursor-based approaches. Request only necessary fields and use server-side filtering when possible.
Testing & Mocking APIs
For unit tests, mock fetch using libraries like fetch-mock or in browser use polyfills. For offline development use static JSON files or tools like JSON Server to simulate REST endpoints locally.
"In all your getting, get understanding." — Proverbs 4:7. Be patient learning async patterns — understanding the event loop is key to mastery.
Knowledge Check — 20 Questions
- Q1. Which method starts an HTTP request in the browser and returns a Promise?
- Q2. What does
response.okindicate?
- Q3. Which keyword pauses execution until a promise resolves?
- Q4. What is CORS primarily for?
- Q5. Which is a safe place to store secret API keys?
- Q6. Which technique reduces the number of API calls while typing search input?
- Q7. What does JSON.parse() do?
- Q8. Which status codes indicate client error?
- Q9. What is a Promise.all used for?
- Q10. Which header is commonly used to pass an API token?
- Q11. Which is best to handle transient network errors?
- Q12. Which method returns the response body as JSON?
- Q13. What is the event loop responsible for?
- Q14. Which pattern avoids storing secrets in the client?
- Q15. What does the term 'rate limit' mean?
- Q16. Which approach helps when an API is slow?
- Q17. Which is a good practice when parsing unknown JSON?
- Q18. What does Promise.race do?
- Q19. Debouncing is most useful for:
- Q20. For offline-friendly apps, which is recommended?