Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Comprehensive Nuxt 3.x reference covering SSR, file-based routing, auto-imports, server routes, and Nitro deployment.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/core-data-fetching.md
1---2name: data-fetching3description: useFetch, useAsyncData, and $fetch for SSR-friendly data fetching4---56# Data Fetching78Nuxt provides composables for SSR-friendly data fetching that prevent double-fetching and handle hydration.910## Overview1112- `$fetch` - Basic fetch utility (use for client-side events)13- `useFetch` - SSR-safe wrapper around $fetch (use for component data)14- `useAsyncData` - SSR-safe wrapper for any async function1516## useFetch1718Primary composable for fetching data in components:1920```vue21<script setup lang="ts">22const { data, status, error, refresh, clear } = await useFetch('/api/posts')23</script>2425<template>26<div v-if="status === 'pending'">Loading...</div>27<div v-else-if="error">Error: {{ error.message }}</div>28<div v-else>29<article v-for="post in data" :key="post.id">30{{ post.title }}31</article>32</div>33</template>34```3536### With Options3738```ts39const { data } = await useFetch('/api/posts', {40// Query parameters41query: { page: 1, limit: 10 },42// Request body (for POST/PUT)43body: { title: 'New Post' },44// HTTP method45method: 'POST',46// Only pick specific fields47pick: ['id', 'title'],48// Transform response49transform: (posts) => posts.map(p => ({ ...p, slug: slugify(p.title) })),50// Custom key for caching51key: 'posts-list',52// Don't fetch on server53server: false,54// Don't block navigation55lazy: true,56// Don't fetch immediately57immediate: false,58// Default value59default: () => [],60})61```6263### Reactive Parameters6465```vue66<script setup lang="ts">67const page = ref(1)68const { data } = await useFetch('/api/posts', {69query: { page }, // Automatically refetches when page changes70})71</script>72```7374### Computed URL7576```vue77<script setup lang="ts">78const id = ref(1)79const { data } = await useFetch(() => `/api/posts/${id.value}`)80// Refetches when id changes81</script>82```8384## useAsyncData8586For wrapping any async function:8788```vue89<script setup lang="ts">90const { data, error } = await useAsyncData('user', () => {91return myCustomFetch('/user/profile')92})93</script>94```9596### Multiple Requests9798```vue99<script setup lang="ts">100const { data } = await useAsyncData('cart', async () => {101const [coupons, offers] = await Promise.all([102$fetch('/api/coupons'),103$fetch('/api/offers'),104])105return { coupons, offers }106})107</script>108```109110## $fetch111112For client-side events (form submissions, button clicks):113114```vue115<script setup lang="ts">116async function submitForm() {117const result = await $fetch('/api/submit', {118method: 'POST',119body: { name: 'John' },120})121}122</script>123```124125**Important**: Don't use `$fetch` alone in setup for initial data - it will fetch twice (server + client). Use `useFetch` or `useAsyncData` instead.126127## Return Values128129All composables return:130131| Property | Type | Description |132|----------|------|-------------|133| `data` | `Ref<T>` | Fetched data |134| `error` | `Ref<Error>` | Error if request failed |135| `status` | `Ref<'idle' \| 'pending' \| 'success' \| 'error'>` | Request status |136| `refresh` | `() => Promise` | Refetch data |137| `execute` | `() => Promise` | Alias for refresh |138| `clear` | `() => void` | Reset data and error |139140## Lazy Fetching141142Don't block navigation:143144```vue145<script setup lang="ts">146// Using lazy option147const { data, status } = await useFetch('/api/posts', { lazy: true })148149// Or use lazy variants150const { data, status } = await useLazyFetch('/api/posts')151const { data, status } = await useLazyAsyncData('key', fetchFn)152</script>153```154155## Refresh & Watch156157```vue158<script setup lang="ts">159const category = ref('tech')160161const { data, refresh } = await useFetch('/api/posts', {162query: { category },163// Auto-refresh when category changes164watch: [category],165})166167// Manual refresh168const refreshData = () => refresh()169</script>170```171172## Caching173174Data is cached by key. Share data across components:175176```vue177<script setup lang="ts">178// In component A179const { data } = await useFetch('/api/user', { key: 'current-user' })180181// In component B - uses cached data182const { data } = useNuxtData('current-user')183</script>184```185186Refresh cached data globally:187188```ts189// Refresh specific key190await refreshNuxtData('current-user')191192// Refresh all data193await refreshNuxtData()194195// Clear cached data196clearNuxtData('current-user')197```198199## Interceptors200201```ts202const { data } = await useFetch('/api/auth', {203onRequest({ options }) {204options.headers.set('Authorization', `Bearer ${token}`)205},206onRequestError({ error }) {207console.error('Request failed:', error)208},209onResponse({ response }) {210// Process response211},212onResponseError({ response }) {213if (response.status === 401) {214navigateTo('/login')215}216},217})218```219220## Passing Headers (SSR)221222`useFetch` automatically proxies cookies/headers from client to server. For `$fetch`:223224```vue225<script setup lang="ts">226const headers = useRequestHeaders(['cookie'])227const data = await $fetch('/api/user', { headers })228</script>229```230231<!--232Source references:233- https://nuxt.com/docs/getting-started/data-fetching234- https://nuxt.com/docs/api/composables/use-fetch235- https://nuxt.com/docs/api/composables/use-async-data236-->237