DEV Community

Adam Miller
Adam Miller

Posted on

4 1

How To Add a Cache Layer to your JavaScript API

Caching can be difficult to get right, but it doesn't really take much code if you are aware of the gotcha's. The sample below implements a simple cache around an API in roughly 10 lines of code:

      const apiCache = new Map();
      async function apiWrapper(url) {
        if (!apiCache.has(url)) {
          const promise = fetch(url).then((r) => r.json());
          apiCache.set(url, promise);
        }

        const response = await apiCache.get(url);
        return cloneDeep(response);
      }

There are a few key points to note with the code above.

It always returns a promise.

You don't want to cache the result, you want to cache the promise. This handles race conditions where multiple callers are requesting the same resource simultaneously. The consuming code behaves the same whether its the 1st call for this resource or the 20th. The promise just resolves more quickly when the resource has already been cached.

The response is cloned before returning to the consumer.

Because Javascript will return a reference, if a caller modifies the response, it will be modified for all future consumers. This has to be a deep clone, using a shallow clone via object spread or assign will cause issues. Use cloneDeep from lodash.

Note that this implementation uses the URL as the cache key. If your api uses http body with a POST, you will want to serialize the data and add it to the url to build a unique key.

Below is a full standalone html page if you want to try it out and verify edge cases:

<html>
  <head> </head>
  <body>
    <script>
      function cloneDeep(obj) {
        //example only, use lodash
        return JSON.parse(JSON.stringify(obj));
      }

      const apiCache = new Map();
      async function apiWrapper(url) {
        if (!apiCache.has(url)) {
          const promise = fetch(url).then((r) => r.json());
          apiCache.set(url, promise);
        }

        const response = await apiCache.get(url);
        return cloneDeep(response);
      }

      const post1Url = "https://jsonplaceholder.typicode.com/posts/1";
      console.log("make 1st request");
      apiWrapper(post1Url)
        .then((post1) => {
          post1.title = "fetched from 1";
          console.log(post1);
        })
        .catch((e) => {
          console.log(e);
        });

      console.log("make 2nd request");
      apiWrapper(post1Url)
        .then((post1) => {
          console.log(post1);
        })
        .catch((e) => {
          console.log(e);
        });
    </script>
  </body>
</html>

Qodo Takeover

Introducing Qodo Gen 1.0: Transform Your Workflow with Agentic AI

While many AI coding tools operate as simple command-response systems, Qodo Gen 1.0 represents the next generation: autonomous, multi-step problem-solving agents that work alongside you.

Read full post

Top comments (0)

SurveyJS custom survey software

JavaScript Form Builder UI Component

Generate dynamic JSON-driven forms directly in your JavaScript app (Angular, React, Vue.js, jQuery) with a fully customizable drag-and-drop form builder. Easily integrate with any backend system and retain full ownership over your data, with no user or form submission limits.

Learn more

Best practices for optimal infrastructure performance with Magento

Running a Magento store? Struggling with performance bottlenecks? Join us and get actionable insights and real-world strategies to keep your store fast and reliable.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤️