Frontend Master

JavaScript Curriculum

Fetch API
+60 XP

Fetch API

medium
~35 min·60 XP

The coffee shop menu is now managed in a CMS. Instead of hardcoding cards in HTML, JavaScript fetches the menu from an API, builds the DOM, and keeps everything in sync — no page reload needed.

Fetch API

fetch() returns a Promise. Combined with async/await, it lets you load data from any server without page reloads. Every modern web app is built on this foundation.

GET, POST, PUT, DELETE

The four HTTP methods — each with its own purpose and request shape:

fetch-builder.js

Read data from an API. No body. Parameters go in the URL as query string.

// Simple GET
const res  = await fetch('https://api.thegrind.com/menu')
const data = await res.json()
console.log(data.items) // → [{id:1, name:'Espresso'...}]

// GET with query params:
const params = new URLSearchParams({ category: 'coffee', sort: 'price' })
const res2 = await fetch(`/api/menu?${params}`)
⚠️fetch() does not throw on HTTP errors
`fetch()` only rejects on network failure (no connection, DNS error). A `404` or `500` response resolves successfully — the Promise fulfills. Always check `res.ok` (true for 200–299) or `res.status` before calling `res.json()`. Otherwise you silently process an error response as if it were data.

Promise States — Visualized

Watch what happens to a Promise through its lifecycle:

promise-states.js
Promise: idle
not started
async function loadMenu() {
  const res  = await fetch('/api/menu')
  if (!res.ok) throw new Error(`HTTP ${res.status}`)
  const data = await res.json()
  renderCards(data.items)
}

loadMenu()
// Click to simulate a fetch()
ℹ️Two awaits for every fetch
Every fetch needs two awaits: one for the network response (`await fetch(url)`), and one to read the body (`await res.json()`). The body is a stream — reading it is also async. Miss the second await and you get a Promise object instead of your data.

Callbacks → .then() → async/await

The evolution of async patterns — and why async/await wins:

async-patterns.js

async/await makes async code look and behave like synchronous code. Errors use familiar try/catch. This is the modern standard — use it for all new code.

// ✓ async/await — reads like sync code
async function loadMenu() {
  try {
    const res      = await fetch('/api/menu')
    if (!res.ok) throw new Error(`HTTP ${res.status}`)
    const data     = await res.json()
    const filtered = await filterItems(data.items)
    renderCards(filtered)
  } catch (err) {
    console.error('Failed:', err)
    showError()
  }
}

loadMenu()
✓ Reads top to bottom like sync code
✓ try/catch for all errors
✓ Easy to debug with breakpoints
✓ Use Promise.all for parallel calls
💡Promise.all for parallel requests
Never chain `await fetch(a)` then `await fetch(b)` when you need both — that's sequential and doubles your wait time. Use `Promise.all([fetch(a), fetch(b)])` to run them simultaneously. The result arrives when the slowest one completes.

Your Challenge

Write loadMenu() using async/await. Fetch /api/menu, check res.ok, parse JSON, render cards with createCard(). Wrap in try/catch — show .error-banner on failure. Then write loadAll() using Promise.all to fetch menu and specials in parallel.

Challenge

Write an async loadMenu() function that fetches '/api/menu', checks res.ok, parses the JSON, and renders cards. Add a try/catch that shows an error banner if the fetch fails. Then write a parallel Promise.all that fetches both the menu and the day's specials simultaneously.

fetchasyncawaitPromisePromise.allres.jsonres.okstatus-codesContent-Typetry-catchasync-error-handling
Fetch API | Nexus Learn