Caching is an important optimization technique and having an in-memory cache can definitely improve the performance. In this article, we will be creating a simple Next.js app and cache the open dog-api data with Redis.

Step 1: Creating Next.js app

To create Next app, run

npx create-next-app caching-redis

Now to start the app, run

npm run dev

Step 2: Measuring performance

We will be using Next.js built in tools for performance measurement. Inside your _app.js file add the following content.

import '../styles/globals.css';

export function reportWebVitals(metric) {
  if (metric.label === 'custom') {
    console.log(metric);
  }
}
function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}

export default MyApp

Now if you reload your page, you'll see something like this on console.

Here, value is defining how long it took the page, in milliseconds, to hydrate on Next.js. Right now, since we don't have anything, it is almost negligible.

Step 3: Integrating open dog API

In the index.js file, paste the following content. We are using the open dog api to fetch a list of Hound dogs.

We are fetching data inside getServerSideProps and passing that to the component through props.

After adding some minimal styles, result will be something like this.

In the console, you will now see that the value has increased to 597ms, as now the data is being fetched from the server every time you reload.

Step 4: Installing Redis

In this article, we will be using Redis as an in-memory cache where we will be storing the response of the API.

To install Redis, we will use Docker containers, because its very simple to use.

docker run -p 6379:6379 --name my-redis -d redis

6379 is the default port for Redis. You have a running instance of Redis locally and accessible at port 6379.

Step 5: Caching the API response

We will be using Redis as a middleware, where we first check if the data exists on Redis, if yes, we send that to the props, if not, we fetch from the server. This way is often called checking for a ‘cache hit-miss’.
We are going to install 2 packages, one is the good old redis and the other one is bluebird , which will promisify all Redis methods.

Copy and replace your index.js file with the following code

import Image from "next/image";
import styles from "../styles/Home.module.css";
import redis from "redis";
import bluebird from "bluebird";

function Home({ dogImages }) {
  return (
    <div className={styles.home}>
      {dogImages.map((url, index) => {
        return (
          <div key={index} className={styles.dogs}>
            <Image
              src={url}
              height="200"
              width="200"
              alt="dogs"
              layout="fixed"
            />
          </div>
        );
      })}
    </div>
  );
}

const fetchData = async (url) => {
  const query = await fetch(url);
  return await query.json();
};

export const getServerSideProps = async () => {
  bluebird.promisifyAll(redis.RedisClient.prototype);
  const cache = redis.createClient();
  let data = {};
  await cache.existsAsync("dogs").then(async (reply) => {
    if (reply !== 1) {
      // cache miss, need to fetch
      data = await fetchData("https://dog.ceo/api/breed/hound/images");
      await cache.set("dogs", JSON.stringify(data));
      console.log("getting dogs from server", data.message);
    } else {
      // cache hit, will get data from redis
      data = JSON.parse(await cache.getAsync("dogs"));
      console.log("getting dogs from redis", data.message);
    }
  });
  return {
    props: {
      dogImages: data.message,
    },
  };
};

export default Home;

Notice how with using bluebird, we effectively promisified the methods, so instead of cache.exists we use, cache.existsAsync.

Now if you reload the app and check the console.

The value has dropped to 538ms. Now, its not much, but it will provide considerable optimization in a large app and the time would be more visible.

Additional Context

For the scope of this article, I did not handle any cache invalidation. For that, you can also set an expiry time once saving the response on Redis, so that the response is always updated.

You can find the code for this on my github here.