Nuxt·

Cache an API response in Nuxt

Learn how to cache an API response in client side and use it everywhere in your app.
Post image

This feature was introduced in Nuxt 3.8 with the getCachedData option.

What is this solving?

By default, when using useFetch or useAsyncData in Nuxt components, the data might be re-fetched every time you navigate back to a component (client-side navigation), even if the data hasn't changed or doesn't need refreshing immediately. This can lead to unnecessary API calls and potentially show different data, when the user might expect it to be consistent within their session.

What getCachedData Does

Introduced in Nuxt 3.8, getCachedData is an option you can pass to useFetch and useAsyncData. It's a function that allows you to define custom client-side caching logic. It essentially acts as a gatekeeper before the actual fetch handler (the API call or async function) is executed.

Example

Here's how I can fetch a user's preferences from the API and cache it using getCachedData, data is fetched on the first load and then cached in the client side.

app.vue
<script setup lang="ts">
const nuxtApp = useNuxtApp();
await useFetch("/api/user-preferences", {
  key: "user-preferences",
  getCachedData(key) {
    return nuxtApp.payload.data[key] || nuxtApp.static.data[key];
  },
});
</script>

Taking it a step further - Adding a TTL to invalidate the cache

You can add a TTL (Time To Live) to the cache, so the data is refreshed after a certain period of time.

app.vue
<script setup lang="ts">
const nuxtApp = useNuxtApp()
const { data } = await useFetch<UserPreferences>('/api/user-preferences', {
  key: 'user-preferences',
  transform(input) {
    return {
      ...input,
      fetchedAt: new Date()
    }
  },
  getCachedData(key) {
    const data = nuxtApp.payload.data[key] || nuxtApp.static.data[key]
    // If data is not fetched yet
    if (!data) {
      // Fetch the first time
      return
    }

    // Check if the data is older than 5 minutes
    const expirationDate = new Date(data.fetchedAt)
    expirationDate.setTime(expirationDate.getTime() + 5 * 60 * 1000) // 5 minutes TTL
    const isExpired = expirationDate.getTime() < Date.now()
    if(isExpired) {
      // Refetch the data
      return
    }

    return data
  },
})
</script>

<template>
  <div>
    <h1>User Preferences</h1>
    <div v-if="data">
      <div>Theme: {{ data.theme }}</div>
      <div>Language: {{ data.language }}</div>
      <div>Notifications: {{ data.notifications ? 'Enabled' : 'Disabled' }}</div>
    </div>
    <NuxtLink to="/settings">Edit Preferences</NuxtLink>
  </div>
</template>

useNuxtData

Once you have the data in the client side, you can use useNuxtData to access it anywhere in your app.

app.vue
const { data: userPreferences } = useNuxtData("user-preferences");

Neat right?


Video

FYI, this video by Alexander Lichter was super helpful to understand this feature better and I highly recommend you watch it.