useAsyncData Basic Usage
Learn how to use the type-safe composables generated by the CLI.
What the CLI Generates
For an OpenAPI endpoint like this:
/pets:
get:
operationId: getPets
responses:
'200':
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Pet'The CLI generates two variants:
Standard Variant
// Generated: composables/useAsyncDataGetPets.ts
import { useApiAsyncData, type ApiAsyncDataOptions } from '../runtime/useApiAsyncData'
export const useAsyncDataGetPets = (
params?: {},
options?: ApiAsyncDataOptions<Pet[]>,
customKey?: string // Optional custom cache key
) => {
// useApiAsyncData wraps Nuxt's useAsyncData under the hood
return useApiAsyncData<Pet[]>('/pets', {
method: 'GET',
...options
}, customKey)
}Raw Variant (with Headers)
// Generated: composables/useAsyncDataGetPetsRaw.ts
import { useApiAsyncDataRaw, type ApiAsyncDataOptions, type RawResponse } from '../runtime/useApiAsyncDataRaw'
export const useAsyncDataGetPetsRaw = (
params?: {},
options?: ApiAsyncDataOptions<RawResponse<Pet[]>>,
customKey?: string
) => {
// Returns full response with headers, status, and data
return useApiAsyncDataRaw<Pet[]>('/pets', {
method: 'GET',
...options
}, customKey)
}How Cache Keys Work
By default, generated composables auto-generate a cache key using operation name + resolved URL + params.
useAsyncDataGetPetById-/pet/1useAsyncDataGetPetById-/pet/2useAsyncDataFindPetsByStatus-/pet/findByStatus-{"status":"available"}useAsyncDataGetInventory-/store/inventory(no params, so no suffix)
This gives independent entries for different URLs/params without extra work.
If you want intentional cache sharing, pass a custom key as the last parameter:
const { data } = useAsyncDataFindPetsByStatus(
{ status: 'available' },
undefined,
'mi-clave'
)Basic Usage
Use the generated composable in your components:
<script setup lang="ts">
// Type-safe composable generated by CLI
const { data: pets, pending, error } = useAsyncDataGetPets()
</script>
<template>
<div v-if="pending">Loading...</div>
<div v-else-if="error">Error: {{ error.message }}</div>
<ul v-else>
<li v-for="pet in pets" :key="pet.id">{{ pet.name }}</li>
</ul>
</template>Nuxt useAsyncData Documentation
Generated composables wrap Nuxt's useAsyncData. For complete documentation on data, pending, error, refresh(), cache keys, and standard options like immediate, watch, server, see:
What the CLI Adds
1. Lifecycle Callbacks
Add hooks to intercept and react to request lifecycle events:
<script setup lang="ts">
const { data: pets } = useAsyncDataGetPets(
undefined,
{
// ⏱️ Before request
onRequest: ({ url, headers }) => {
console.log('Fetching from:', url)
return {
headers: {
...headers,
'X-Request-ID': crypto.randomUUID()
}
}
},
// ✅ On success (2xx response)
onSuccess: (pets) => {
console.log(`Loaded ${pets.length} pets`)
showToast('Pets loaded successfully', 'success')
},
// ❌ On error (4xx/5xx or network error)
onError: (error) => {
console.error('Failed to load pets:', error)
if (error.status === 404) {
showToast('No pets found', 'error')
}
},
// 🏁 Always runs (success or error)
onFinish: ({ success }) => {
console.log('Request complete:', success ? 'success' : 'failed')
}
}
)
</script>2. Reactive Params — Auto-refresh on Change
CLI Addition — Not in Native Nuxt
Nuxt's native useAsyncData does not auto-detect reactive dependencies inside the fetch function. It requires you to manually declare watch sources, and even then it doesn't know how to re-evaluate the URL or params.
The generated composables solve this: pass a ref or computed as params and the composable wires everything up automatically.
Pass a ref or computed as params — when it changes, the composable re-fetches with the new values:
Path parameters:
<script setup lang="ts">
const petId = ref(1)
// Reactively fetches /pet/1, then /pet/2 when petId changes
const { data: pet } = useAsyncDataGetPetById(
computed(() => ({ petId: petId.value }))
)
function loadNext() {
petId.value++ // triggers automatic re-fetch
}
</script>Query parameters:
<script setup lang="ts">
const status = ref('available')
// Re-fetches whenever status changes
const { data: pets } = useAsyncDataFindPetsByStatus(
computed(() => ({ status: status.value }))
)
</script>
<template>
<select v-model="status">
<option>available</option>
<option>pending</option>
<option>sold</option>
</select>
</template>Disable auto-refresh if you want to control when the re-fetch happens:
<script setup lang="ts">
const petId = ref(1)
const { data: pet, refresh } = useAsyncDataGetPetById(
computed(() => ({ petId: petId.value })),
{ watch: false } // petId changes won't trigger a re-fetch
)
async function loadPet(id: number) {
petId.value = id
await refresh() // you decide when to fetch
}
</script>Plain objects still work exactly as before — backward compatible:
// Works just as before — no reactivity, fetches once
const { data } = useAsyncDataGetPetById({ petId: 1 })3. Pick
Select specific fields from the response without transform:
<script setup lang="ts">
// Only get id, name, and status
const { data: pets } = useAsyncDataGetPets(
undefined,
{
pick: ['id', 'name', 'status']
}
)
// data is Ref<Array<{ id: number, name: string, status: string }>>
</script>3. Response Headers (Raw Variant)
Access full response headers and status codes:
<script setup lang="ts">
const { data: response } = useAsyncDataGetPetsRaw()
watch(response, (res) => {
if (res) {
console.log('Status:', res.status) // 200
console.log('Headers:', res.headers) // Headers object
console.log('Total:', res.headers.get('X-Total-Count'))
console.log('Data:', res.data) // Pet[]
}
})
</script>
<template>
<div>
<p>Status: {{ response?.status }}</p>
<p>Total Items: {{ response?.headers.get('X-Total-Count') }}</p>
<ul>
<li v-for="pet in response?.data" :key="pet.id">
{{ pet.name }}
</li>
</ul>
</div>
</template>CLI Addition
Important: Nuxt's native useAsyncData does NOT return response headers or status codes. This is a feature added by the CLI's Raw variant.
More Features
For additional features like transform, global headers, baseURL, and more, see Shared Features →
Examples with Callbacks
Simple GET Request
<script setup lang="ts">
const { data: pets } = useAsyncDataGetPets(
undefined,
{
onSuccess: (pets) => {
console.log(`Loaded ${pets.length} pets`)
}
}
)
</script>POST Request with Success Callback
<script setup lang="ts">
const form = ref({ name: '', status: 'available' })
const { execute: createPet, pending } = useAsyncDataCreatePet(
{ body: form.value },
{
immediate: false, // Nuxt option: don't execute on mount
onSuccess: (pet) => {
showToast(`Created pet: ${pet.name}`, 'success')
navigateTo(`/pets/${pet.id}`)
form.value = { name: '', status: 'available' }
},
onError: (error) => {
showToast(`Failed: ${error.message}`, 'error')
}
}
)
</script>
<template>
<form @submit.prevent="createPet">
<input v-model="form.name" placeholder="Pet name" />
<button type="submit" :disabled="pending">Create</button>
</form>
</template>Request Interception
<script setup lang="ts">
const { data: pets } = useAsyncDataGetPets(
undefined,
{
onRequest: ({ headers, params }) => {
return {
headers: {
...headers,
'X-Client-Version': '1.0.0'
},
params: {
...params,
timestamp: Date.now()
}
}
}
}
)
</script>Error Handling
<script setup lang="ts">
const { data, error, status } = useAsyncDataGetPets()
// Different handling based on status
const errorMessage = computed(() => {
if (!error.value) return null
switch (error.value.status) {
case 404:
return 'No pets found'
case 401:
return 'Please log in'
case 500:
return 'Server error, please try again'
default:
return error.value.message
}
})
</script>
<template>
<div>
<div v-if="status === 'pending'">Loading...</div>
<div v-else-if="error" class="error">
{{ errorMessage }}
</div>
<ul v-else>
<li v-for="pet in data" :key="pet.id">{{ pet.name }}</li>
</ul>
</div>
</template>SSR Considerations
<script setup lang="ts">
// Runs on server and client
const { data } = useAsyncDataGetPets({}, {
server: true
})
// Only runs on client
const { data: clientData } = useAsyncDataGetUserPets({}, {
server: false
})
</script>Pick Specific Fields
const { data: pets } = useAsyncDataGetPets(
undefined,
{
pick: ['id', 'name', 'status']
}
)
// Only id, name, and status are returned