Skip to content

Handling JSON Responses

Most public APIs respond with JSON. Once you have parsed the response with response.json(), you have a regular JavaScript value — but you still need to navigate the response shape to find what you need, and handle cases where the shape is not what you expected.

You have a JavaScript object after response.json() resolves. Use destructuring immediately to pull out exactly what you need:

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(({ rates, base_code, time_last_update_utc }) => {
console.log(`Base: ${base_code}`);
console.log(`Updated: ${time_last_update_utc}`);
console.log(`EUR: ${rates.EUR}`);
console.log(`GBP: ${rates.GBP}`);
});

Destructuring in the .then callback parameter is idiomatic — you name only what you need and the rest of the response is ignored.

With a rates object, converting is simple multiplication:

function convertAmount(amount, fromCurrency, toCurrency, rates) {
if (fromCurrency === toCurrency) return amount;
const inUSD = amount / rates[fromCurrency];
return inUSD * rates[toCurrency];
}
// If rates are relative to USD:
convertAmount(10, 'USD', 'EUR', rates); // 10 * 0.9183 = 9.183
convertAmount(10, 'GBP', 'EUR', rates); // (10 / 0.7897) * 0.9183 ≈ 11.63

If the rates object does not contain a currency code you request, the result is undefined. Multiply with undefined and you get NaN. Guard against this:

function convertAmount(amount, fromCurrency, toCurrency, rates) {
const fromRate = rates[fromCurrency];
const toRate = rates[toCurrency];
if (!fromRate || !toRate) throw new Error(`Unknown currency: ${fromCurrency} or ${toCurrency}`);
return (amount / fromRate) * toRate;
}

The ExchangeRate-API includes a result field. When the request succeeds it is 'success'; on failure it is 'error' with an error-type field. You can check this for more specific error handling:

fetch('https://open.er-api.com/v6/latest/USD')
.then(response => {
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
})
.then(data => {
if (data.result !== 'success') {
throw new Error(`API error: ${data['error-type'] ?? 'unknown'}`);
}
return data.rates;
})
.then(rates => {
// safe to use rates here
})
.catch(error => console.error(error.message));

If the server returns a response that is not valid JSON, response.json() rejects. The .catch handler will receive that error. Nothing special needed — the chain handles it.

It is good practice to transform the raw API response into the shape your app actually needs before passing it to other parts of the code. Create a parseRates function that extracts only the currencies BudgetBuddy displays:

const SUPPORTED_CURRENCIES = ['USD', 'EUR', 'GBP', 'CAD', 'JPY', 'AUD'];
function parseRates(data) {
const { rates } = data;
return SUPPORTED_CURRENCIES.reduce((acc, code) => {
if (rates[code] !== undefined) acc[code] = rates[code];
return acc;
}, {});
}
fetch('https://open.er-api.com/v6/latest/USD')
.then(response => {
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
})
.then(parseRates)
.then(rates => console.log(rates));
// { USD: 1, EUR: 0.9183, GBP: 0.7897, CAD: 1.3421, JPY: 145.23, AUD: 1.5310 }

parseRates is passed directly as the .then callback — it receives the parsed JSON object and returns only the currencies you need.

  1. Fetch https://open.er-api.com/v6/latest/USD and destructure rates, base_code, and time_last_update_utc from the parsed JSON.
  2. Write convertAmount(amount, from, to, rates) with proper undefined guards. Test converting $100 USD → EUR and £50 GBP → USD.
  3. Write parseRates(data) that returns only USD, EUR, GBP, CAD, and JPY.
  4. Chain parseRates directly as a .then callback.
  • Destructure the parsed JSON in the .then callback to extract only what you need.
  • Currency conversion is (amount / fromRate) * toRate when rates are relative to USD.
  • Always guard against missing currency codes — undefined rates produce NaN.
  • Check data.result === 'success' for API-level errors distinct from HTTP errors.
  • Transform raw API responses into your app’s data shape as early as possible in the chain.