#Fetching data in Pinia store - useFetch make sense here?

26 messages · Page 1 of 1 (latest)

fringe kayak
#

I have Nuxt app with Pinia. In one store, I find myself using useFetch for data fetching (with SSR support), then expose the status, error and another computed variable which does some computation on the data returned.

# stores/store-a.ts
export const useStoreA = defineStore("storeA", () => {
  const {
   data,
   status,
   error,
   refresh,
  } = useFetch<Items[]>("/api/items", { lazy: true })

  const myItems: Ref<Item[]> = computed(() => {
   const itemData = data.value || []
   .. some more logic ..
   return itemData
  })

  return {
   data,
   status,
   error,
   refresh,
  }
})

This provides pretty damn clean API, with status and error baked in.

But when I looked for examples of Pinia with useFetch, I couldn't find any. All the AI GTPs suggest exposing a function to fetch data (e.g, fetchItems()), while using $fetch.

Am I missing something here? Any reason to not useFetch on store setup?

thin yokeBOT
#

Hi @fringe kayak. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

Using useFetch directly inside a Pinia store's setup function is technically possible and can provide a clean API with reactive status, error, and data handling, as you've described. However, there are some important caveats and potential pitfalls to be aware of, especially in the context of SSR and reactivity.

Key Considerations

  1. SSR and Reactivity Issues
    The Nuxt documentation and community discussions highlight that useFetch is designed to be called directly in a component setup function, plugin, or route middleware—not typically inside a Pinia store. This is because useFetch leverages Nuxt's SSR payload system to transfer data from server to client and to avoid double-fetching during hydration. When used inside a store, especially outside of a component context, you may encounter issues where the data is not available as expected, or reactivity does not work as intended on the first request. This is discussed in this Stack Overflow answer, where the only reliable way to access the response data in a store was via an interceptor, not directly from the returned refs.
#
  1. Best Practice: Expose Fetch Functions
    Most examples and recommendations suggest exposing a fetch function (e.g., fetchItems) in your store, which internally uses $fetch or useFetch when called from a component. This approach gives you more control over when and how data is fetched, and avoids the SSR/hydration pitfalls that can arise from using useFetch directly in the store's setup function. The Nuxt docs and several Stack Overflow answers recommend using $fetch for client-only actions or POST requests, and useFetch/useAsyncData for SSR-friendly, component-level data fetching. See Nuxt Data Fetching Docs and this Stack Overflow discussion for more details.

  2. Reactivity and Awaiting Data
    When useFetch is used in a store, you may find that the data is not immediately available, even if you use async/await. This is due to the SSR nature of Nuxt and how the payload is managed. As noted in the Stack Overflow answer, "the data is not available via await, this is related to the SSR nature of the response" and you may need to use interceptors or other workarounds to access the data reliably in a store context.

Example from the Docs

The Nuxt docs and examples consistently show useFetch being used in component setup functions, not in Pinia stores:

<script setup lang="ts">
const { data, status, error, refresh } = await useFetch('/api/modules', {
  pick: ['title']
})
</script>

Nuxt useFetch API Docs

Summary

#
  • It is possible to use useFetch in a Pinia store, but it is not the recommended or most robust approach due to SSR and reactivity caveats.
  • Best practice is to expose a fetch function in your store and call useFetch or $fetch from the component, or use $fetch directly in the store for client-only actions.
  • If you need SSR support and want to avoid double-fetching, use useFetch or useAsyncData in the component, not in the store setup.

If you want to keep your API clean and SSR-safe, consider moving the useFetch call to the component and exposing a fetch function in your store instead.

I'm sorry, but there are no official examples or documentation recommending the use of useFetch directly in a Pinia store's setup function, and there are known caveats with this approach regarding SSR and reactivity. For robust SSR support and predictable reactivity, use useFetch in components and expose fetch functions in your stores instead.

References:


Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@fringe kayak Kapa.ai is still learning and improving, please let me know how I did by reacting below.

cedar oar
#

and use useAsyncData when you call the function

fringe kayak
#

Can you show a simple example? the above code works but I'm afraid I'm missing something

#

I also want SSR on this endpoint, so data is fetched server-side and hydrated on the client without additional call

cedar oar
#
const items = []
const getItems = async () => {
 return await $fetch(...)
}
// in a component or plugin
await useAsyncData(async () => {
  const response = await store.getItems()
  store.items = response

  // need to return something iirc for useAsyncData to not duplicate call on client
  return true
})```
fringe kayak
#

Do you mind elaborate why it's not recommended to use useFetch in a store? What's the problem with it?

cedar oar
#

useStore isn't

fringe kayak
#

So it might have glitches and stuff?

cedar oar
#

yeah

fringe kayak
#

so in store, using $fetch and in component use useAsyncData and it enables SSR support?

cedar oar
#

yes

#

don't forget about the last comment tho

#

need to return something

fringe kayak
#

Also - is fetching data in component and updating store from the component is a good pattern?
Or trigger the fetch on the store, and wrap the logic of updating state in it

cedar oar
#

it's up to you

#

u can do

// in store
const getItems = async () => {
 const response = await $fetch(...)
 items.value = response
}```
#

so now in component u just to call it

#

but also return true

#
// in a component or plugin
await useAsyncData(async () => {
  await store.getitems()
  return true
})```