Data Fetching Methods
Each endpoint gives you two composables. They differ in what they return and in who decides when the request goes out.
TIP
Both follow the same patterns as Nuxt's native useFetch and $fetch. If you know Nuxt data fetching, you know these.
Comparison
useMyApiData | $myApi | |
|---|---|---|
| Returns | AsyncData | A promise of the response data |
| On failure | A reactive error value | Throws |
| Caching | Payload cache on by default, with deduplication | Payload cache off unless opted in |
| Server rendering | Hydrates automatically | You decide where it runs |
| Reach for it | Page and component data | Mutations and programmatic calls |
Reactive Data Fetching
Use useMyApiData composables in components and pages. They hook into Nuxt's hydration and caching, so server-rendered data is not fetched a second time on the client.
<script setup lang="ts">
const postId = ref(1)
// The request automatically updates when `postId` changes
const { data } = await useJsonPlaceholderData(() => `posts/${postId.value}`, {
watch: [postId]
})
function nextPost() {
postId.value++
}
</script>
<template>
<div>
<h1>{{ data?.title }}</h1>
<button @click="nextPost()">
Next Post
</button>
</div>
</template>Direct API Calls
Use $myApi composables for programmatic API interactions, form submissions, and one-time actions where you need direct control over request timing.
Using $myApi With useAsyncData
Calling $myApi multiple times inside useAsyncData causes Nuxt context errors on the server. Use useMyApiData composables instead for reactive data, or see the $myApi documentation for workarounds.
Common pattern for handling form submissions:
<script setup lang="ts">
import type { FetchError } from 'ofetch'
const form = ref({
title: '',
body: '',
userId: 1
})
const isSubmitting = ref(false)
const submitError = ref(null)
async function submitPost() {
isSubmitting.value = true
submitError.value = null
try {
const newPost = await $jsonPlaceholder('posts', {
method: 'POST',
body: form.value
})
console.log('Post created:', newPost)
// Redirect or show success message
}
catch (error) {
submitError.value = (error as FetchError).statusMessage || 'Failed to create post'
}
finally {
isSubmitting.value = false
}
}
</script>
<template>
<form @submit.prevent="submitPost">
<input v-model="form.title" placeholder="Title" required>
<textarea v-model="form.body" placeholder="Content" required />
<button type="submit" :disabled="isSubmitting">
{{ isSubmitting ? 'Creating...' : 'Create Post' }}
</button>
<div v-if="submitError" class="error">
{{ submitError }}
</div>
</form>
</template>In plugins or middleware, use $myApi for initial data fetching:
export default defineNuxtPlugin(async () => {
const settings = useState('app.settings', () => ({}))
// Fetch app settings once and hydrate state in the client
try {
const data = await $jsonPlaceholder('settings')
settings.value = data
}
catch (error) {
console.error('Failed to load settings:', error)
}
})Next Steps
- Module Configuration – Every option an endpoint accepts.
- OpenAPI Integration – Infer request and response types from a schema.