Caching API requests in JavaScript
Tags: caching, javascript, api, fetch
Published: 2020-01-19 20:36:18

---
title: Caching API requests in JavaScript
published: true
description: "How to implement a caching strategy to prevent unnecessary requests in JavaScript"
tags: caching, javascript, api, fetch
---

[TLDR;](https://www.youtube.com/watch?v=uw6mWGNLhlo)

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](https://openweathermap.org/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.

```html
<!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>
```

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

```javascript
const cache = {
    london: {
        ...metadata,
        cacheTimer: 1234567890
    },
    ...
}
```

Our regular function to fetch data would be something like:

```javascript
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
}
```

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:

```javascript
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]
}
```

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:

```javascript
function getCacheTimer(time) {
  const now = new Date().getTime()
  if (cacheTimer < now + time) {
    cacheTimer = now + time
  }
  return cacheTimer
}
```

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.

```javascript
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()
```

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!