Let's cache data in Next.js

This post has been translated into:Spanish

One of the problems that we have when working with server-side in Next.js is that we don't have access to the API from the browser in build time. This means, we can't use localStorage, sessionStorage and a large etc. in the server.

I was facing this same problem when I was developing the About section in this blog. The main idea was to call to the Last.fm API once per week to retrieve my 10 top songs of the week. So, I would only call again to the API if an entire week has passed.

I tried many solutions trying to access the API, but nothing worked. Then I came up with this: to store the data in a local cache file.

So, I wasn't sure at first if it was going to work in production, because we're using the file system. But yeah, it does ✨✨✨.

import fs from 'fs';
import path from 'path';
/** Utils */
import { hasReachedAWeek } from '@utils/date';

const TOP_TRACKS_CACHE_PATH = path.join(process.cwd(), '.tracks');

export const fetchTopTracks = async () => {
  let cachedData;
  let tracks = [];

  // Check if the data is cached
  try {
    cachedData = JSON.parse(fs.readFileSync(TOP_TRACKS_CACHE_PATH, 'utf8') ?? null);
  } catch (error) {
    console.error(error);
  }

  // If data is cached and a week has not yet passed, use cached data
  if (!hasReachedAWeek(cachedData?.updatedAt)) {
    return cachedData.data;
  }

  // If not, fetch the data from the Last.fm API and store it in the cache file
  try {
    tracks = (await getTopTracks()) || [];

    const now = new Date();

    fs.writeFileSync(
      TOP_TRACKS_CACHE_PATH,
      JSON.stringify({ updatedAt: now.getTime(), data: tracks }),
      { flag: 'w', encoding: 'utf8' }
    );
  } catch (error) {
    console.error(error);
  }

  return tracks;
};

Now, anywhere we want to use the cache data, we can import it and use it inside the getStaticProps method:

import { fetchTopTracks } from '@lib/tracks';
export const getStaticProps = async () => {
  const tracks = (await fetchTopTracks()) ?? [];

  return {
    props: {
      tracks,
    },
  };
};