Skip to content

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.

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.

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 string
  • response.blob() — parse as a binary Blob (for images, files)

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));

fetch accepts an optional second argument — a configuration object — for anything beyond a simple GET:

// POST request with a JSON body
fetch('/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.

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.

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.

  1. Use fetch to request https://open.er-api.com/v6/latest/USD and log the full response object (before calling .json()).
  2. Chain .json() and log data.rates.EUR and data.rates.GBP.
  3. Add a response.ok check that throws a descriptive error if the status is not 2xx.
  4. Add .catch and .finally to complete the chain.
  5. 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 a Response object.
  • response.ok is true for 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, and body.
  • Cache the result of slow or rate-limited API calls to avoid redundant requests.