N
Nuxt2w ago
td2t

Repository pattern + useAsyncData + Pinia store, hydration + caching problems?

We follow this article to write all our API calls https://medium.com/@luizzappa/nuxt-3-repository-pattern-organising-and-managing-your-calls-to-apis-with-typescript-acd563a4e046 so we can fetch a resource like so const { data: documents } = await $api.users.getUploadedDocuments(); but we actually populate the reactive state in a pinia store, persisted in Cookie, const documentList = ref<DocumentMetadata[]>([]);, we use this documentList ref in our components. However we notice random cached requests, random network requests and sometimes double hydration from certain requests too, what to do?
Medium
Nuxt 3 | Repository pattern: organising and managing your calls to ...
In Nuxt 2 the use of axios was straightforward and allowed a good management of API calls. When I started to develop with Nuxt 3 I found it…
2 Replies
kapa.ai
kapa.ai2w ago
Hi @td2t. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- You’re hitting known Nuxt 3 “gotchas” around useAsyncData/useFetch caching + side effects + Pinia. From the sources: * useFetch is literally useAsyncData + $fetch, with built‑in caching keyed by the key argument and request parameters. [Which to use] * This cache means: * Nuxt may not re‑fetch if it thinks the call is the “same” (same key, same URL/params). [Nuxt shows old data] * Forcing a unique key (e.g. Date.now()) disables caching and always re‑fetches. [Nuxt shows old data] * Recent PRs improved client‑side getCachedData so cached data is reused beyond hydration unless you manually refresh. [getCachedData cache] * useAsyncData is not intended for side effects like mutating a Pinia store; this can cause repeated executions and unexpected behaviour when the result is nullish/undefined. [useAsyncData side effects docs] * Nuxt core explicitly optimises to avoid re‑running a handler when another useAsyncData with the same key is already active, and to consistently share cached data across instances. [don’t rerun asyncdata; respect cachedData] * There’s also work on controlling execution (ready, immediate: false, reacting to key changes, etc.), which again assumes you’re using it as a pure data fetch, not as a place for arbitrary side effects. [ready option; key + immediate false] Given that, your current pattern:
const { data: documents } = await $api.users.getUploadedDocuments()
// inside that, a useAsyncData or useFetch…
documentList.value = documents // Pinia state
const { data: documents } = await $api.users.getUploadedDocuments()
// inside that, a useAsyncData or useFetch…
documentList.value = documents // Pinia state
…is very likely: * depending on useAsyncData/useFetch cache semantics and on store side effects at the same time, and * being called from multiple places/components during SSR + hydration. That combination perfectly explains “random”: * cached vs network requests (Nuxt’s cache rules + timing), * double hydration or apparently duplicated calls (SSR + client, multiple components), * inconsistent store state (side effects re‑run when Nuxt decides to refetch). Recommendations based on the sources 1. Stop using useAsyncData/useFetch as a driver for Pinia side effects The Nuxt docs PR is explicit that this usage is problematic: useAsyncData is for fetching data, not for triggering store mutations. [useAsyncData side effects docs] Instead: * Fetch data with useAsyncData/useFetch in the component and bind it directly, or * Fetch in the Pinia store using a plain $fetch/$fetch.raw (no asyncData), then assign to documentList. In other words, either:
// Component
const { data: documents } = await useFetch('/api/documents')

// Component
const { data: documents } = await useFetch('/api/documents')

or:
// Pinia action
async loadDocuments() {
const docs = await $fetch('/api/documents')
this.documentList = docs
}

// Pinia action
async loadDocuments() {
const docs = await $fetch('/api/documents')
this.documentList = docs
}

but don’t use useAsyncData inside the store and then also rely on its caching. 2. Control or disable caching when you really want fresh data If a particular fetch must always hit the network (e.g. user‑specific, mutable data), give useFetch/useAsyncData a changing key so Nuxt doesn’t reuse cache. [Nuxt shows old data]
const { data } = await useFetch('/api/documents', {
key: `documents-${Date.now()}`
})

const { data } = await useFetch('/api/documents', {
key: `documents-${Date.now()}`
})

This is crude but effective for debugging whether the cache is the root cause. 3. Use cached data intentionally, not implicitly Newer Nuxt behaviour returns cached data for a given asyncData key as long as there’s no manual refresh, including beyond hydration. [getCachedData cache] That means: * If you want shared, cached data across components, keep using the same key and don’t mutate global store state from inside the handler; just consume data directly from useAsyncData/useFetch. * If you want per‑usage fetches or per‑user data, either: * vary the key (e.g. include user id), or * skip asyncData caching entirely and use plain $fetch inside your repository. 4. Avoid mixing “repository pattern” composables with Pinia persistence + asyncData caching The Medium article pattern isn’t in our knowledge sources, but from what you described, your $api functions probably wrap useAsyncData/useFetch and then your store persists results in cookies. Based on Nuxt’s own guidance on useAsyncData side effects and caching, the safer split is: * Repository composables: small wrappers around plain $fetch only. * useAsyncData / useFetch: used at the component level (or composables that are purely about fetching), returning their data to the caller without mutating global state. * Pinia: owns state and persistence; actions call repository methods ($fetch) and assign results. That removes the triangle of “asyncData cache ↔ repository ↔ store side effects” which is the main source of the randomness you’re seeing. --- The knowledge sources don’t give a one‑line “fix this repository pattern” recipe, but they clearly point to: * useAsyncData/useFetch should be treated as pure data fetchers with caching semantics. * Avoid putting Pinia mutations / side effects inside them. * When behaviour looks random, first eliminate asyncData caching from the equation (unique keys or plain $fetch), then reintroduce caching where you explicitly want it. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@413148786836373544> Kapa.ai is still learning and improving, please let me know how I did by reacting below.

Did you find this page helpful?