Fix: asyncData Should Return an Object in Nuxt

Error message:
asyncData should return an object. Received: {type}
data fetching 2025-01-25

What Causes This Error?

This error occurs when your useAsyncData handler function returns something other than an object (like a primitive value, array, or undefined).

The Problem

<script setup>
// ❌ Wrong - returns a string
const { data } = await useAsyncData('user', () => {
  return 'John'  // Error: should return an object
})

// ❌ Wrong - returns undefined
const { data } = await useAsyncData('user', async () => {
  const user = await fetchUser()
  // Missing return statement!
})

// ❌ Wrong - returns an array directly
const { data } = await useAsyncData('users', () => {
  return ['John', 'Jane']  // Arrays are technically objects but not recommended
})
</script>

The Fix

Return an Object

<script setup>
// ✅ Correct - return an object
const { data } = await useAsyncData('user', () => {
  return { name: 'John' }
})

// ✅ Correct - wrap primitives in an object
const { data } = await useAsyncData('count', async () => {
  const count = await fetchCount()
  return { count }
})

// ✅ Correct - wrap arrays in an object
const { data } = await useAsyncData('users', async () => {
  const users = await fetchUsers()
  return { users }
})
</script>

Common Patterns

Fetching a Single Value

<script setup>
// ❌ Wrong
const { data } = await useAsyncData('title', () => $fetch('/api/title'))
// If API returns just a string, this fails

// ✅ Correct - wrap the response
const { data } = await useAsyncData('title', async () => {
  const title = await $fetch('/api/title')
  return { title }
})

// Access it
console.log(data.value?.title)
</script>

Fetching Multiple Values

<script setup>
// ✅ Correct - return object with multiple properties
const { data } = await useAsyncData('dashboard', async () => {
  const [users, posts, stats] = await Promise.all([
    $fetch('/api/users'),
    $fetch('/api/posts'),
    $fetch('/api/stats')
  ])

  return { users, posts, stats }
})

// Access them
console.log(data.value?.users)
console.log(data.value?.posts)
console.log(data.value?.stats)
</script>

Using useFetch

Note: useFetch handles this automatically since API responses are typically objects:

<script setup>
// ✅ useFetch works because APIs usually return objects
const { data } = await useFetch('/api/user')  // { name: 'John', email: '...' }

// If your API returns a primitive, use transform:
const { data } = await useFetch('/api/count', {
  transform: (count) => ({ count })
})
</script>

With Transform

<script setup>
// ✅ Transform to ensure object structure
const { data } = await useAsyncData('users', () => $fetch('/api/users'), {
  transform: (users) => ({
    users,
    total: users.length
  })
})
</script>

Handling Different Response Types

API Returns Object (Good)

<script setup>
// API returns { name: 'John', email: 'john@example.com' }
const { data } = await useAsyncData('user', () => $fetch('/api/user'))
// ✅ Works fine
</script>

API Returns Array

<script setup>
// API returns ['item1', 'item2']
const { data } = await useAsyncData('items', async () => {
  const items = await $fetch('/api/items')
  return { items }  // ✅ Wrap in object
})

// Access: data.value?.items
</script>

API Returns Primitive

<script setup>
// API returns 42
const { data } = await useAsyncData('count', async () => {
  const count = await $fetch('/api/count')
  return { count }  // ✅ Wrap in object
})

// Access: data.value?.count
</script>

API Returns null

<script setup>
// API might return null
const { data } = await useAsyncData('user', async () => {
  const user = await $fetch('/api/user')
  return { user }  // ✅ Wrap even if null
})

// Access: data.value?.user (will be null)
</script>

Async/Await Pitfalls

Missing Return

<script setup>
// ❌ Wrong - no return
const { data } = await useAsyncData('user', async () => {
  const user = await $fetch('/api/user')
  // Oops! No return statement
})

// ✅ Correct - return the data
const { data } = await useAsyncData('user', async () => {
  const user = await $fetch('/api/user')
  return { user }
})
</script>

Conditional Return

<script setup>
// ❌ Wrong - might return undefined
const { data } = await useAsyncData('user', async () => {
  const user = await $fetch('/api/user')
  if (user) {
    return { user }
  }
  // Returns undefined if no user!
})

// ✅ Correct - always return an object
const { data } = await useAsyncData('user', async () => {
  const user = await $fetch('/api/user')
  return { user: user || null }
})
</script>

TypeScript Best Practices

<script setup lang="ts">
interface User {
  id: number
  name: string
}

interface UserData {
  user: User | null
}

// ✅ Type-safe approach
const { data } = await useAsyncData<UserData>('user', async () => {
  const user = await $fetch<User>('/api/user')
  return { user }
})

// data.value?.user is typed as User | null
</script>

Destructuring in Template

<script setup>
const { data } = await useAsyncData('dashboard', async () => {
  const [user, settings] = await Promise.all([
    $fetch('/api/user'),
    $fetch('/api/settings')
  ])
  return { user, settings }
})
</script>

<template>
  <div v-if="data">
    <h1>{{ data.user?.name }}</h1>
    <p>Theme: {{ data.settings?.theme }}</p>
  </div>
</template>

Quick Checklist

  • Handler function returns an object, not a primitive
  • Arrays are wrapped in an object: { items: [...] }
  • Primitives are wrapped: { value: 123 }
  • Always have a return statement
  • Conditional paths all return objects
  • Use TypeScript for type safety