Next.js Discord

Discord Forum

Confused about Next.js 15 caching

Unanswered
Mozzy posted this in #help-forum
Open in Discord
Avatar
I understand that in 15, the default cache is no-store, but now I cannot opt into caching at all in some cases.

The following code are server actions. getTopTracks is called from a server page.tsx
const getAccessToken = async () => {
  const response = await fetch(TOKEN_ENDPOINT, {
    method: "POST",
    headers: {
      Authorization: `Basic ${basic}`,
      "Content-Type": "application/x-www-form-urlencoded",
    },
    body: querystring.stringify({
      grant_type: "refresh_token",
      refresh_token,
    }),
  });

  return response.json();
};

export const getTopTracks = async (range: string) => {
  const { access_token } = await getAccessToken();

  const url = new URL("https://api.spotify.com/v1/me/top/tracks");

  // Excluding medium_term as it's the default
  const validRange = ["short_term", "long_term"].includes(range);
  if (validRange) url.searchParams.append("time_range", range);

  const res = await fetch(url, {
    headers: {
      Authorization: `Bearer ${access_token}`,
    },
    // next: { revalidate: 86_400 },
    // With the above or with the below, this is never cached
    cache: "force-cache",
  });

  return res.ok
    ? ((await res.json()) as SpotifyApi.UsersTopTracksResponse)
    : null;
};

I am able to use cache: "force-cache" on getAccessToken, but adding it to getTopTracks leads to it always skipping cache when the page is navigated to
 │ GET https://api.spotify.com/v1/me/top/tracks 200 in 288ms (cache skip)
 │ │ Cache skipped reason: (cache-control: no-cache (hard refresh))

Any pointers would be helpful

5 Replies

Avatar
Where is cache-control: no-cache (hard refresh) coming from and why does the cache property not override this?
Avatar
I read that staleTimes have been set to 0 as the default. I assume that's where "hard refresh" comes from. But why would the cache: "force-cache" on the fetch() not be followed?
Avatar
You are setting the Authorization header on the fetch call. This will never be cached.
Avatar
Would this be correct then?
export const getTopTracks = unstable_cache(
  async (range: string) => {
    console.log("Fetching top tracks");

    const { access_token } = await getAccessToken();

    const url = new URL("https://api.spotify.com/v1/me/top/tracks");

    // Excluding medium_term as it's the default
    const validRange = ["short_term", "long_term"].includes(range);
    if (validRange) url.searchParams.append("time_range", range);

    const res = await fetch(url, {
      headers: {
        Authorization: `Bearer ${access_token}`,
      },
    });

    return res.ok
      ? ((await res.json()) as SpotifyApi.UsersTopTracksResponse)
      : null;
  },
  [],
  // 3 seconds as a test. Seems to work
  { revalidate: 3  },
);
It seems to work, but I'm unsure if that's how to do it