N
Nuxt3mo ago
lazygopher

Fetching data in Pinia store - useFetch make sense here?

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,
}
})
# 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?
14 Replies
kapa.ai
kapa.ai3mo ago
Hi @lazygopher. 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. 2. 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. 3. 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>
<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:
- Nuxt Data Fetching Docs
- Nuxt useFetch API Docs
- Stack Overflow: Http GET request via Pinia action only works on second attempt in Nuxt app
- Stack Overflow: Which to use: $fetch, useAsyncData or useFetch for GET and POST requests in Nuxt 3? ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3mo ago
<@693201794137981049> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Cake
Cake3mo ago
no, use $fetch and use useAsyncData when you call the function
lazygopher
lazygopherOP3mo ago
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
Cake
Cake3mo ago
const items = []
const getItems = async () => {
return await $fetch(...)
}
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
})
// 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
})
lazygopher
lazygopherOP3mo ago
Do you mind elaborate why it's not recommended to use useFetch in a store? What's the problem with it?
Cake
Cake3mo ago
because useFetch is supposed to be called in nuxt hooks or components useStore isn't
lazygopher
lazygopherOP3mo ago
So it might have glitches and stuff?
Cake
Cake3mo ago
yeah
lazygopher
lazygopherOP3mo ago
so in store, using $fetch and in component use useAsyncData and it enables SSR support?
Cake
Cake3mo ago
yes don't forget about the last comment tho need to return something
lazygopher
lazygopherOP3mo ago
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
Cake
Cake3mo ago
it's up to you u can do
// in store
const getItems = async () => {
const response = await $fetch(...)
items.value = response
}
// 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
})
// in a component or plugin
await useAsyncData(async () => {
await store.getitems()
return true
})
lazygopher
lazygopherOP3mo ago
And if I want to have lazy, so on the UX I get loading indicator, should I do it without await? right now with the useFetch it shows loading indicator

Did you find this page helpful?