Nuxt 3 cache and pagination with useNuxtData

1.8k views Asked by At

Since latest Nuxt 3 version, cache has to be manually handled using useNuxtData() composable.

This code is an extract of what I do to cache a single detail post, calling an API endpoint.

const post = ref({})
// Get cached post using slug as cache key 
const cachedPost = useNuxtData(route.params.slug)

// If post is cached, use it, otherwise fetch it
if (cachedPost.data.value) {
  post.value = cachedPost.data.value
} else {
  const { data, error } = await useFetch(`/public/posts/${route.params.slug}`, {
      key: route.params.slug
  })

  post.value = data.value
}

Now I want to show a list of category posts using cursor pagination, so I'm doing this

const categoryData = ref({})
const cachedCategoryData = useNuxtData(`${route.params.category}${lastId.value ? '-'+lastId.value : ''}`)
const lastId = computed(() => route.query.lastid)

// If category is cached, use it, otherwise fetch it
if (cachedCategoryData.data.value) {
  categoryData.value = cachedCategoryData.data.value
} else {
  const { data, error } = await useFetch(`/public/category/${route.params.category}`,
  {
    key: `${route.params.category}${lastId.value ? '-'+lastId.value : ''}`,
    watch: [lastId],
    query: { lastid: lastId }
  })

  categoryData.value = data.value
}

The problem I'm facing is that despite watcher works and it fetches the data again when lastId changes, results are not being propagated to categoryData.

Any advice will be appreciated

1

There are 1 answers

0
VonC On

I see from your code:

const cachedCategoryData = useNuxtData(`${route.params.category}${lastId.value ? '-'+lastId.value : ''}`)
const lastId = computed(() => route.query.lastid)

In JavaScript, order matters. Variables should be declared before they are used. Your lastId is computed based on the route query, which is declared after it is used in cachedCategoryData. That is a potential source of the issue you are seeing.

const lastId = computed(() => route.query.lastid)
const cacheKey = computed(() => `${route.params.category}${lastId.value ? '-'+lastId.value : ''}`)
const cachedCategoryData = useNuxtData(cacheKey.value)

And, still from your code:

const { data, error } = await useFetch(`/public/category/${route.params.category}`, {
  key: `${route.params.category}${lastId.value ? '-'+lastId.value : ''}`,
  watch: [lastId],
  query: { lastid: lastId }
})

Plus, Nuxt (v3) automatically watches for route changes, including changes in query parameters. So, If lastId is a route query parameter, you do not need to watch it in your useFetch call

const { data, error } = await useFetch(`/public/category/${route.params.category}`, {
  key: cacheKey.value,
  query: { lastid: lastId.value }
})

Also, make sure to handle potential errors during fetch calls. That way, you can determine whether the issue is due to a failed API call or some other issue in your code. Here, make sure your API endpoint /public/category/${route.params.category} responds with the correct data and HTTP status code.

Revised code addition:

if (error.value) {
  console.error(`Error fetching data: ${error.value}`)
  // Handle error appropriately
  return
}

Finally, check if you are updating the route query lastid correctly as you paginate. If it is not updated correctly, lastId computed property will not change and hence the watcher will not trigger useFetch.

Here is the revised code, taking into account those remarks:

const categoryData = ref({})
const lastId = computed(() => route.query.lastid)
const cacheKey = computed(() => `${route.params.category}${lastId.value ? '-'+lastId.value : ''}`)

const cachedCategoryData = useNuxtData(cacheKey.value)

// If category is cached, use it, otherwise fetch it
if (cachedCategoryData.data.value) {
  categoryData.value = cachedCategoryData.data.value
} else {
  const { data, error } = await useFetch(`/public/category/${route.params.category}`, {
    key: cacheKey.value,
    query: { lastid: lastId.value }
  })

  if (error.value) {
    console.error(`Error fetching data: ${error.value}`)
    // Handle error appropriately
    return
  }

  categoryData.value = data.value
}

If the issue still persists, consider debugging each step of your application's flow to find where the data is not being updated as expected.


As noted in "Nuxt2 - watch route change", With Nuxt 3, you can use onBeforeRouteUpdate from vue-router.

The onBeforeRouteUpdate method is a Vue Router lifecycle hook, and it allows you to execute some logic before the current route changes, but while the component instance is being reused (e.g., when route parameters change).

In your case, the main issues in your original code were related to the order of declarations and the unnecessary watching of lastId in useFetch.
Since you are using the useFetch method provided by Nuxt, which already includes reactive features and should trigger a fetch whenever a watched reactive source changes, onBeforeRouteUpdate may not be necessary.