#Fetch only once on initial page load

19 messages · Page 1 of 1 (latest)

sullen imp
#

I have an endpoint from my backend, with important data like seo, settings etc. I fetch it server side but I notice on navigation it refetch on client side, is there a way to prevent this? I want to fetch it only on first load.

signal hollowBOT
#

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

#

Yes, there are several ways to prevent refetching data on client-side navigation in Nuxt. Let me explain your options:

Using callOnce

The callOnce utility (available since Nuxt v3.9) is designed specifically for your use case - executing code only once during the initial render:

const websiteConfig = useState('config')

await callOnce(async () => {
  websiteConfig.value = await $fetch('<https://my-backend.com/api/settings'>)
})

This will only run once during server-side rendering and won't run again during hydration or client-side navigation Call Once.

Using useFetch with a key

When using useFetch, you can control caching behavior by providing a specific key:

const { data: settings } = await useFetch('/api/settings', {
  key: 'global-settings'
})

The data will be cached with this key, and subsequent navigations will use the cached data instead of refetching Docs > Getting Started > Data Fetching.

Using a plugin with useState

Another approach is to fetch the data in a plugin and store it in a global state:

// plugins/settings.js
export default defineNuxtPlugin(async () => {
  const settings = useState('settings', () => null)
  
  if (!settings.value) {
    const { data } = await useFetch('/api/settings')
    settings.value = data.value
  }
})

Then you can access this state anywhere in your application without refetching.

Preventing cache invalidation

If you're still experiencing refetching, make sure you're not inadvertently invalidating the cache. Some users have reported issues with useFetch caching that were resolved by:

#
  1. Ensuring consistent key usage
  2. Not using dynamic values in the key that change on navigation
  3. Avoiding the refresh function unless explicitly needed

If you're still having issues, you might need to check if something in your code is calling refreshNuxtData or clearNuxtData which would invalidate the cache Stack Overflow Post.

Would you like me to elaborate on any of these approaches?

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

#

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

sullen imp
#

@signal hollow but I already use key 'global-settings' and it still refetch it on the client side on navigation

import type { Settings } from './types';

export const getSettings = () => {
  // We call this api method in app.vue which is not re-render on locale switch
  // so we manually watch changing this cookie to trigger re-fetch with new locale
  const locale = useCookie('i18n_redirected');
  return useAsyncData<{ data: { settings: Settings } }>(
    'global-settings',
    () => useNuxtApp().$apiFetch('/settings'),
    {
      watch: [locale],
      lazy: true,
      server: true,
    },
  );
};
signal hollowBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

I see the issue with your implementation. Let me explain why your data is being refetched on client-side navigation and how to fix it.

The problem is likely related to the watch option in your useAsyncData call. When you watch the locale cookie, it will trigger a refetch whenever that value changes, including during client-side navigation.

Here are a few approaches to solve this:

1. Use getCachedData option

You can use the getCachedData option to control when data is fetched:

import type { Settings } from './types';

export const getSettings = () => {
  const locale = useCookie('i18n_redirected');
  const nuxtApp = useNuxtApp();
  
  return useAsyncData<{ data: { settings: Settings } }>(
    'global-settings',
    () => nuxtApp.$apiFetch('/settings'),
    {
      watch: [locale],
      lazy: true,
      server: true,
      getCachedData: (key) => {
        // Only return null (triggering a fetch) when locale changes
        const cachedData = nuxtApp.payload.data[key] || nuxtApp.static.data[key];
        return cachedData;
      }
    },
  );
};

2. Use useLazyAsyncData with conditional refresh

Another approach is to use useLazyAsyncData and only refresh when the locale changes:

import type { Settings } from './types';

export const getSettings = () => {
  const locale = useCookie('i18n_redirected');
  const previousLocale = ref(locale.value);
  
  const result = useLazyAsyncData<{ data: { settings: Settings } }>(
    'global-settings',
    () => useNuxtApp().$apiFetch('/settings'),
    { server: true }
  );
  
  watch(locale, (newLocale) => {
    if (newLocale !== previousLocale.value) {
      previousLocale.value = newLocale;
      result.refresh();
    }
  });
  
  return result;
};

3. Access cached data with useNuxtData

#

You can also use useNuxtData to access the cached data without triggering a refetch:

// First fetch in a plugin or app.vue
const { data } = await useAsyncData(
  'global-settings',
  () => useNuxtApp().$apiFetch('/settings')
);

// Then in your components or pages
const { data: settings } = useNuxtData('global-settings');

This approach lets you access the cached data without refetching it Docs > Api > Composables.

The key issue is that the watch option is causing refetches during navigation. By controlling when refreshes happen or by accessing cached data directly, you can prevent unnecessary refetches while still updating when the locale changes.

Would you like me to explain any of these approaches in more detail?

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

#

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

sullen imp
#

@signal hollow I need to fetch data for hero section on server side and refetch data on locale change but also cache data between pages

import type { HeroSlide } from './types';

export const getHeroSliders = () => {
  const locale = useCookie('i18n_redirected');
  const nuxtApp = useNuxtApp();

  return useAsyncData<{ data: HeroSlide[] }>(`hero-sliders`, () => nuxtApp.$apiFetch('/sliders'), {
    watch: [locale],
    // lazy: true,
    server: true,
    getCachedData: (key) => {
      return nuxtApp.payload.data[key] || nuxtApp.static.data[key];
    },
  });
};

and on page where get data

const { data: heroSliders } = await getHeroSliders();

but nuxt loading indicator always shows on locale change and data is not change for new locale

signal hollowBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

I see the issue you're facing with your hero slider data fetching. Let's address both problems: refetching on locale change while maintaining caching between pages, and fixing the loading indicator issue.

The main issue is that your current implementation doesn't properly handle locale changes. Let me explain how to fix it:

The Problem

  1. Your watch on the locale cookie is triggering refetches, but the data isn't updating for the new locale
  2. The Nuxt loading indicator shows during locale changes, suggesting a navigation/refetch is happening

Solution

Here's an improved implementation:

import type { HeroSlide } from './types';

export const getHeroSliders = () => {
  const locale = useCookie('i18n_redirected');
  const route = useRoute();
  const localeQuery = computed(() => locale.value);
  
  return useAsyncData<{ data: HeroSlide[] }>(
    () => `hero-sliders-${locale.value}`, // Dynamic key based on locale
    () => useNuxtApp().$apiFetch('/sliders'),
    {
      watch: [localeQuery], // Watch the computed property
      server: true,
      dedupe: 'cancel', // Cancel any pending requests when a new one is made
    }
  );
};

Key changes:

  1. Dynamic key with locale: By including the locale in the key (hero-sliders-${locale.value}), you ensure each locale has its own cached data
  2. Using a computed property: This ensures proper reactivity when watching the locale
  3. Using dedupe option: This helps manage concurrent requests during locale changes

Alternative Approach

If you still face issues, you can try this more explicit approach:

import type { HeroSlide } from './types';
#

export const getHeroSliders = () => {
const locale = useCookie('i18n_redirected');
const nuxtApp = useNuxtApp();

const { data, refresh } = useAsyncData<{ data: HeroSlide[] }>(
() => hero-sliders-${locale.value},
() => nuxtApp.$apiFetch('/sliders'),
{ server: true }
);

// Explicitly watch locale changes and refresh data
watch(() => locale.value, () => {
refresh();
});

return { data, refresh };
};


This approach gives you more control over when refreshes happen [Docs > Getting-started > Data Fetching](<https://nuxt.com/docs/getting-started/data-fetching#refresh-and-execute>).

## Regarding the Loading Indicator

The loading indicator appears because Nuxt is detecting a data fetching operation during navigation. To prevent this, you could:

1. Use `lazy: true` to avoid blocking navigation
2. Consider implementing a custom loading state for just the hero section instead of the global loading indicator

If you need to keep the data fetching behavior but hide the loading indicator specifically during locale changes, you could conditionally disable the loading indicator when detecting a locale change.

Let me know if you need further clarification on any of these approaches!
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------
#

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

sullen imp
#

@signal hollow is it possible to avoid fetch on client side?

import type { HeroSlide } from './types';

export const getHeroSliders = () => {
return useAsyncData<{ data: HeroSlide[] }>(
hero-sliders,
() => useNuxtApp().$apiFetch('/sliders'),
{
lazy: true,
server: true,
},
);
};

signal hollowBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

Yes, it's possible to avoid fetching on the client side while still getting the data from the server. Let me explain how to achieve this with your code.

Your current implementation uses server: true, which is good, but there are a few issues that might be causing client-side fetches:

  1. The lazy: true option means the fetch won't block navigation, but it doesn't prevent client-side fetching
  2. You're not explicitly preventing client-side execution

Here's how you can modify your code to ensure it only fetches on the server side:

import type { HeroSlide } from './types';

export const getHeroSliders = () => {
  return useAsyncData<{ data: HeroSlide[] }>(
    `hero-sliders`,
    () => useNuxtApp().$apiFetch('/sliders'),
    {
      server: true,
      lazy: false, // Make it blocking to ensure server-side fetch completes
      client: false // This explicitly prevents client-side fetching
    },
  );
};

The key addition here is the client: false option, which tells Nuxt to never execute this fetch on the client side Docs > Getting-started > Data Fetching.

Alternatively, if you want to keep the non-blocking behavior of lazy: true, you can use this approach:

import type { HeroSlide } from './types';

export const getHeroSliders = () => {
  // Only execute on the server
  if (process.server) {
    return useAsyncData<{ data: HeroSlide[] }>(
      `hero-sliders`,
      () => useNuxtApp().$apiFetch('/sliders'),
      {
        lazy: true,
        server: true
      },
    );
  }
  
  // On client, just return the cached data
  return useNuxtData<{ data: HeroSlide[] }>(`hero-sliders`);
};
#

This approach uses process.server to conditionally execute the fetch only on the server, and on the client it uses useNuxtData to access the cached data without triggering a new fetch Docs > Api > Composables > use-fetch.

Remember that when using useAsyncData with proper caching, the data fetched on the server will be automatically transferred to the client in the payload, so you shouldn't need to fetch it again on the client side.

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