Caching API requests in JavaScript

adancarrasco

Adán Carrasco

Posted on January 19, 2020

Caching API requests in JavaScript

TLDR;

Consuming APIs is everyday's meal in JavaScript, this also has its limitations, amount of requests per second is the most common, in this case we are going to implement an algorithm to cache data based on time. Let's say we are sure that the data we are going to fetch is not going to change in a certain period of time then we can cache the data for that span. Some data can be cached by seconds, minutes, or even days.

For this example we are going to consume data from Open Weather API, this is an API to fetch the weather in different cities, for the weather we can say that it won't change every 10 minutes, so we can cache the data for this period of time.

Let's prepare our simple template to input the city and show the data on blur.

<!DOCTYPE html>
<html>
  <head>
    <title>Cache</title>
    <meta charset="UTF-8" />
  </head>

  <body>
    <div id="app">
      <h1>Hello Cache!</h1>
      <div>
        <input type="text" id="inputWeather" placeholder="Fetch weather for" />
      </div>
      <div id="container"></div>
    </div>

    <script src="src/index.js"></script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Our cache structure should store the metadata that we want to cache + the timestamp until the data will be cached, to make it easier to get/set the structure will be a HashMap. The key can be the city in lower case (if your keys can be duplicated you can use a more complex key).

const cache = {
    london: {
        ...metadata,
        cacheTimer: 1234567890
    },
    ...
}
Enter fullscreen mode Exit fullscreen mode

Our regular function to fetch data would be something like:

async function fetchWeatherInfo(cityName) {
  let weatherInfo = await fetch(
    `https://api.openweathermap.org/data/2.5/weather?q=${cityName}&APPID=${api}`
  )
    .then(data => data.json())
    .then(myJson => myJson)
  return weatherInfo
}
Enter fullscreen mode Exit fullscreen mode

Where we receive the city name and we do the fetch, returning the metadata. This function will be wrapped by the cache function, in the cache function we also receive the cityName + the time until the cache will be valid, if the hash key doesn't exist or if the time is lower than now then we are going to fetch the new data and cache it, the function will be like:

const cache = {}
let cacheTimer = 0

async function fetchWithCache(cityName, time) {
  const now = new Date().getTime()
  if (!cache[cityName] || cache[cityName].cacheTimer < now) {
    cache[cityName] = await fetchWeatherInfo(cityName)
    cache[cityName].cacheTimer = getCacheTimer(time)
  }
  return cache[cityName]
}
Enter fullscreen mode Exit fullscreen mode

Inside the fetchWithCache function we are getting the cache timer, the cache timer is now's date + the time we want our data to be cached. Let's create the function to get the cache timer:

function getCacheTimer(time) {
  const now = new Date().getTime()
  if (cacheTimer < now + time) {
    cacheTimer = now + time
  }
  return cacheTimer
}
Enter fullscreen mode Exit fullscreen mode

At this point we have the function to normally fetch our data, our function to set the timer we want our data to be cached and our function to cache the data. Let's create the function that will display the weather data in the HTML. To display data in the HTML we need to get the input element and set an event listener for the on change. The cacheTime is the value that we want our data to be persisted for, in this case is 100,000 milliseconds. In the event listener we are calling the displayWeatherData function, this function will call our cache function and get the data either from the cache or from the API request.

const input = document.getElementById("inputWeather")
const weatherContainer = document.getElementById("container")
const cacheTime = 100000

function init() {
  input.addEventListener("change", updateValue)
  function updateValue(e) {
    displayWeatherData(e.target.value)
  }
}

async function displayWeatherData(cityName) {
  const weatherInfo = await fetchWithCache(cityName.toLowerCase(), cacheTime)
  if (!weatherInfo || !weatherInfo.weather) {
    weatherContainer.innerHTML = `There's an error with request.`
    return
  }
  weatherContainer.innerHTML = `<p>${weatherInfo.name}</p><p>${weatherInfo.weather[0].main}<p><p>${weatherInfo.main.temp}</p><p>--------------</p>`
  console.log(cache)
}

init()
Enter fullscreen mode Exit fullscreen mode

For debugging purposes I left the console.log statement you can check the Network tab in your browser DevTools and confirm that the request is only performed the first time, then it's cached for 10 seconds.

You can see it in action here: https://codesandbox.io/s/kind-dew-5bbrn

Thanks for reading!

💖 💪 🙅 🚩
adancarrasco
Adán Carrasco

Posted on January 19, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Caching API requests in JavaScript
caching Caching API requests in JavaScript

January 19, 2020