JavaScript

Data & APIs

Pull live data into your page with fetch() — users, quotes, weather, anything. The magic that makes a capstone feel real. Below: the fetch pattern, a working real API call, dynamic lists, and the four states every data request needs to handle.

The pattern

How fetch() works

You ask a URL for data, wait for it to arrive, turn it into a JavaScript object, then put it on the page. Because it takes time, you use async/await and always wrap it in try/catch.

JavaScript
async function loadData() {
  try {
    const res = await fetch('https://dummyjson.com/quotes/random');  // 1. ask the URL
    if (!res.ok) throw new Error('HTTP ' + res.status);              // 2. check it worked
    const data = await res.json();                                   // 3. parse JSON → object
    render(data);                                                    // 4. show it
  } catch (err) {
    showError(err.message);                                          // network/parse failed
  }
}
Live · real API

Fetch a real quote

This actually calls a public API over the internet when you click. Watch the loading spinner, then the result — or an error message if the network is down.

Click the button to fetch a random quote.

HTML
<button class="btn" id="quoteBtn">Get a quote</button>
<div id="quoteOut">
  <div class="quote-box"><p>Click the button to fetch a random quote.</p></div>
</div>
CSS
.btn { border: none; background: #0ea5e9; color: #fff; font-weight: 700; padding: 11px 20px; border-radius: 9px; cursor: pointer; }
.quote-box { background: #f0f9ff; border-left: 4px solid #0ea5e9; border-radius: 0 10px 10px 0; padding: 16px 18px; margin-top: 14px; }
.quote-box p { font-style: italic; color: #0c4a6e; }
.quote-box cite { display: block; margin-top: 8px; font-style: normal; color: #0369a1; font-weight: 700; font-size: .82rem; }
.spinner { width: 30px; height: 30px; border: 4px solid #e5e7eb; border-top-color: #0ea5e9; border-radius: 50%; animation: spin .8s linear infinite; margin: 14px 0; }
@keyframes spin { to { transform: rotate(360deg); } }
.error { color: #b91c1c; }
JavaScript
const btn = document.getElementById('quoteBtn');
const out = document.getElementById('quoteOut');

btn.addEventListener('click', async () => {
  out.innerHTML = '<div class="spinner"></div>';           // loading state
  try {
    const res = await fetch('https://dummyjson.com/quotes/random');
    if (!res.ok) throw new Error('Request failed');
    const q = await res.json();
    out.innerHTML = `<div class="quote-box"><p>"${q.quote}"</p><cite>— ${q.author}</cite></div>`;  // success
  } catch (e) {
    out.innerHTML = '<p class="error">Could not load. Try again.</p>';   // error state
  }
});
Live · the four states

Render a list — and handle every state

Real requests don't always go smoothly. A good fetch handles loading, success, empty (it worked but there's no data), and error. Try each below (these are simulated so you can see all four reliably):

Press a button above.
HTML
<button class="btn" id="loadOk">Load users</button>
<div id="usersOut"><div class="state">Press the button above.</div></div>
CSS
.btn { border: none; background: #0ea5e9; color: #fff; font-weight: 700; padding: 11px 20px; border-radius: 9px; cursor: pointer; }
.ulist { list-style: none; display: grid; gap: 8px; padding: 0; margin-top: 14px; }
.ucard { display: flex; align-items: center; gap: 12px; background: #f6f7f9; border-radius: 10px; padding: 10px 14px; }
.ucard .av { width: 38px; height: 38px; border-radius: 50%; background: linear-gradient(135deg,#0ea5e9,#8b5cf6); color: #fff; display: flex; align-items: center; justify-content: center; font-weight: 700; flex-shrink: 0; }
.ucard .nm { font-weight: 600; color: #111; font-size: .9rem; }
.ucard .em { color: #6b7280; font-size: .78rem; }
.state { text-align: center; color: #6b7280; padding: 18px; }
.state.empty { color: #9ca3af; } .state.error { color: #b91c1c; }
.spinner { width: 30px; height: 30px; border: 4px solid #e5e7eb; border-top-color: #0ea5e9; border-radius: 50%; animation: spin .8s linear infinite; margin: 14px auto; }
@keyframes spin { to { transform: rotate(360deg); } }
JavaScript
const out = document.getElementById('usersOut');

// your real fetch — here we pull sample users from a free, no-key API
async function getUsers() {
  const res = await fetch('https://jsonplaceholder.typicode.com/users');
  if (!res.ok) throw new Error('HTTP ' + res.status);
  return res.json();
}

async function loadUsers() {
  out.innerHTML = '<div class="spinner"></div>';            // 1. LOADING
  try {
    const users = await getUsers();
    if (users.length === 0) {                                // 2. EMPTY
      out.innerHTML = '<div class="state empty">No users yet.</div>';
      return;
    }
    out.innerHTML = '<ul class="ulist">' + users.map(u =>     // 3. SUCCESS — map data → HTML
      `<li class="ucard"><span class="av">${u.name[0]}</span>
         <span><span class="nm">${u.name}</span><br><span class="em">${u.email}</span></span></li>`).join('') + '</ul>';
  } catch (e) {
    out.innerHTML = '<div class="state error">Something went wrong.</div>';  // 4. ERROR
  }
}

document.getElementById('loadOk').addEventListener('click', loadUsers);
Try these

Free, no-key public APIs

All of these are free, need no API key, and allow browser requests (CORS) — perfect for a capstone. Just fetch() the URL.

Free · no key

Random quotes

dummyjson.com/quotes/random

Inspirational quotes with author.

Free · no key

Advice

api.adviceslip.com/advice

A random piece of advice.

Free · no key

Cat facts

catfact.ninja/fact

A random fact about cats.

Free · no key

Countries

restcountries.com/v3.1/all

Flags, capitals, population, region.

Free · no key

Weather

api.open-meteo.com/v1/forecast

Live forecast by lat/long.

Free · no key

Recipes

themealdb.com/api/json/v1/1/random.php

A random meal with ingredients.

Free · no key

GitHub users

api.github.com/users/octocat

Public profile data & avatars.

Free · no key

Fake data

jsonplaceholder.typicode.com/posts

Dummy posts/users for practice.

Capstone idea: a weather dashboard, a country explorer, a random-recipe app, or a quote-of-the-day — each is one fetch() plus the list-rendering pattern above. Combine with widgets from Interactive Features (search/filter the results!).
Recap

The takeaway

fetch(url)await res.json() → put it on the page. Always use async/await inside try/catch, and always design the four states: loading, success, empty, and error. Master this one pattern and you can pull any live data into a capstone.