The Fetch API
fetch is the browser’s built-in API for making HTTP requests. It is Promise-based, flexible, and works for any server that accepts HTTP requests — including the ExchangeRate-API you will use in BudgetBuddy.
The simplest fetch
Section titled “The simplest fetch”A basic GET request needs only the URL:
fetch('https://open.er-api.com/v6/latest/USD') .then(response => response.json()) .then(data => console.log(data));This is the ExchangeRate-API endpoint BudgetBuddy will use. It returns a JSON object with exchange rates for the USD base currency. No API key required.
The Response object
Section titled “The Response object”fetch resolves with a Response object — not the data itself. The Response has metadata about the HTTP response:
fetch('https://open.er-api.com/v6/latest/USD') .then(response => { console.log(response.status); // 200 console.log(response.ok); // true (status 200–299) console.log(response.headers.get('Content-Type')); // 'application/json' return response.json(); // parse the body }) .then(data => console.log(data.rates));response.json() parses the body as JSON — it returns a new Promise that resolves with the parsed JavaScript value.
Other parsing methods:
response.text()— parse as a text stringresponse.blob()— parse as a binary Blob (for images, files)
Always check response.ok
Section titled “Always check response.ok”fetch only rejects on network failure — a DNS error, no connection, the request timing out. A 400 Bad Request or 500 Internal Server Error response is still a fulfilled Promise. You must check response.ok yourself:
fetch('https://open.er-api.com/v6/latest/USD') .then(response => { if (!response.ok) { throw new Error(`Exchange rate API returned ${response.status}`); } return response.json(); }) .then(data => { const { rates, base, time_last_update_utc } = data; console.log(`Rates as of ${time_last_update_utc}`); console.log(`EUR: ${rates.EUR}`); }) .catch(error => console.error('Failed to load rates:', error.message));Request options
Section titled “Request options”fetch accepts an optional second argument — a configuration object — for anything beyond a simple GET:
// POST request with a JSON bodyfetch('/api/expenses', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ description: 'Coffee', amount: 4.50 }),});BudgetBuddy only uses GET requests (fetching rates from the ExchangeRate-API), so you will not need POST in this course. But knowing the pattern is useful for when you do.
The ExchangeRate-API response shape
Section titled “The ExchangeRate-API response shape”Here is an abbreviated version of what the ExchangeRate-API returns:
{ "result": "success", "base_code": "USD", "time_last_update_utc": "Thu, 15 Jan 2024 00:00:02 +0000", "rates": { "USD": 1, "EUR": 0.9183, "GBP": 0.7897, "JPY": 145.23, "CAD": 1.3421 }}You care about rates — the object that maps currency codes to their exchange rates relative to USD.
Caching responses
Section titled “Caching responses”For BudgetBuddy, you do not want to fetch rates on every render — that would be slow and unnecessary. Fetch once, cache the result, and reuse it:
let cachedRates = null;
function getRates() { if (cachedRates) return Promise.resolve(cachedRates); return fetch('https://open.er-api.com/v6/latest/USD') .then(response => { if (!response.ok) throw new Error(`API error: ${response.status}`); return response.json(); }) .then(data => { cachedRates = data.rates; return cachedRates; });}Promise.resolve(value) creates an already-fulfilled Promise — a clean way to return a synchronous value from a function that normally returns a Promise, keeping the API consistent.
Exercise
Section titled “Exercise”- Use
fetchto requesthttps://open.er-api.com/v6/latest/USDand log the full response object (before calling.json()). - Chain
.json()and logdata.rates.EURanddata.rates.GBP. - Add a
response.okcheck that throws a descriptive error if the status is not 2xx. - Add
.catchand.finallyto complete the chain. - Implement the
getRates()caching function above. Call it twice consecutively — confirm the network request only fires once.
fetch(url)returns a Promise that resolves with aResponseobject.response.okistruefor 200–299 status codes — check it manually and throw for errors.response.json()parses the body and returns a Promise — chain it after the ok check.- POST and other methods require a second argument with
method,headers, andbody. - Cache the result of slow or rate-limited API calls to avoid redundant requests.