Cached api call with expiry time

Implement a function in JavaScript that caches the API response for the given amount of time. If a new call is made between that time, the response from the cache will be returned, else a fresh API call will be made.

Example

const call = cachedApiCall(1500);

// first call
// an API call will be made and its response will be cached
call('https://jsonplaceholder.typicode.com/todos/1', {}).then((a) => console.log(a));
//"making new api call"
/*
{
  "userId": 1,
  "id": 1,
  "title": "delectus aut autem",
  "completed": false
}
*/

// cached response will be returned
// it will be quick
setTimeout(() => {
  call('https://jsonplaceholder.typicode.com/todos/1', {}).then((a) => console.log(a));
}, 700);
/*
{
  "userId": 1,
  "id": 1,
  "title": "delectus aut autem",
  "completed": false
}
*/

// a fresh API call is made
// as time for cached entry is expired
setTimeout(() => {
  call('https://jsonplaceholder.typicode.com/todos/1', {}).then((a) => console.log(a));
}, 2000);
//"making new api call"
/*
{
  "userId": 1,
  "id": 1,
  "title": "delectus aut autem",
  "completed": false
}
*/

We can implement this function by forming a closure. The outer function will accept the time and return an async inner function, that will accept the arguments to make the API call.

In the inner function, we will create a new unique key from the arguments to cached value.

Using this key, get the entry from the cache. If there is no entry present or the time of the entry is expired, make a new API call. Else return the value of the entry.

To generate the key and make the API call we will be using two helper functions.

Generating unique key

// helper function to create a key from the input
const generateKey = (path, config) => {
  const key = Object.keys(config)
    .sort((a, b) => a.localeCompare(b))
    .map((k) => k + ":" + config[k].toString())
    .join("&");
  return path + key;
};

Make API call

// helper function to make api call
const makeApiCall = async (path, config) => {
  try{
    let response = await fetch(path, config);
    response = await response.json();
    return response;
  }catch(e){
    console.log("error " + e);
  }
  
  return null;
};

Main function to cache API call

const cachedApiCall = (time) => {
  // to cache data
  const cache = {};
  
  // return a new function
  return async function(path, config = {}) {
    // get the key
    const key = generateKey(path, config);
    
    // get the value of the key
    let entry = cache[key];
  
    // if there is no cached data
    // or the value is expired
    // make a new API call
    if(!entry || Date.now() > entry.expiryTime){
      console.log("making new api call");
      
      // store the new value in the cache
      try {
        const value = await makeApiCall(path, config)
        cache[key] = { value, expiryTime: Date.now() + time };
      }catch(e){
       console.log(error); 
      }
    }
    
    //return the cache
    return cache[key].value;
  }
};
const call = cachedApiCall(1500);

// first call
// an API call will be made and its response will be cached
call('https://jsonplaceholder.typicode.com/todos/1', {}).then((a) => console.log(a));
//"making new api call"
/*
{
  "userId": 1,
  "id": 1,
  "title": "delectus aut autem",
  "completed": false
}
*/

// cached response will be returned
// it will be quick
setTimeout(() => {
  call('https://jsonplaceholder.typicode.com/todos/1', {}).then((a) => console.log(a));
}, 700);
/*
{
  "userId": 1,
  "id": 1,
  "title": "delectus aut autem",
  "completed": false
}
*/

// a fresh API call is made
// as time for cached entry is expired
setTimeout(() => {
  call('https://jsonplaceholder.typicode.com/todos/1', {}).then((a) => console.log(a));
}, 2000);
//"making new api call"
/*
{
  "userId": 1,
  "id": 1,
  "title": "delectus aut autem",
  "completed": false
}
*/