This composable provides a convenient wrapper around `useAsyncData` and `$fetch`. It automatically generates a key based on URL and fetch options, provides type hints for request url based on server routes, and infers API response type.
This composable behaves identically to `useFetch` with the `lazy: true` option set. In other words, the async function does not block navigation. That means you will need to handle the situation where the data is `null` (or whatever value you have provided in a custom `default` factory function).
In brief, `useFetch` receives a URL and gets that data, whereas `useAsyncData` might have more complex logic. `useFetch(url)` is nearly equivalent to `useAsyncData(url, () => $fetch(url))` - it's developer experience sugar for the most common use case.
This composable behaves identically to `useAsyncData` with the `lazy: true` option set. In other words, the async function does not block navigation. That means you will need to handle the situation where the data is `null` (or whatever value you have provided in a custom `default` factory function).
If for some reason you are not satisfied with how the requested data is structured, you may want to use the `transform` option from `useFetch` or `useAsyncData` to alter the data by using a function to perform the necessary transformation after your request has been resolved.
We very much recommend that you remove any data or properties that you do not need, in order to reduce your page's payload size - see the [Minimize Payload](#minimize-payload) section.
### Example
```vue
<scriptsetup>
const { data: users } = await useAsyncData(
"users",
() => $fetch('/api/users'),
{
transform: (users) =>
users.map((user) => ({
id: user.id,
fullName: `${user.firstName} ${user.surname}`,
})),
}
);
</script>
<template>
<div>
<ul>
<liv-for="user in users":key="user.id">
{{ user.fullName }}
</li>
</ul>
</div>
</template>
```
If you are dealing with a complex data structure and you are only interested in a few properties, simply make use of the `pick` option to only pick specific properties you are interested in.
```vue
<scriptsetup>
const { data: user } = await useFetch('/api/users/123', {
Sometimes throughout the course of your user's page visit, you may need to refresh the data loaded from the API. This can happen if the user chooses to paginate, filter results, search, etc.
By default, `refresh()` will cancel any pending requests their result will not update the data or pending state. Any previously awaited promises will not resolve until this new request resolves. You can prevent this behaviour by setting the `dedupe` option, which will instead return the promise for the currently-executing request, if there is one.
Nuxt 3 provides a way to perform `asyncData` fetching within the Options API. You must wrap your component definition within `defineNuxtComponent` for this to work.
There are instances where you may need to directly call the API. Nuxt 3 provides a globally available `$fetch` method using [unjs/ofetch](https://github.com/unjs/ofetch) (in addition to `fetch`) with the same API as the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch).
Using `$fetch` has a number of benefits, including:
It will handle 'smartly' making direct API calls if it's running on the server, or making a client-side call to your API if it's running on the client. (It can also handle calling third-party APIs.)
Plus, it comes with convenience features including automatically parsing responses and stringifying data.
When we call `$fetch` in the browser, user headers like `cookie` will be directly sent to the API. But during server-side-rendering, since the `$fetch` request takes place 'internally' within the server, it doesn't include the user's browser cookies, nor does it pass on cookies from the fetch response.
The example below adds the request headers to an isomorphic `$fetch` call to ensure that the API endpoint has access to the same `cookie` header originally sent by the user.
Be very careful before proxying headers to an external API and just include headers that you need. Not all headers are safe to be bypassed and might introduce unwanted behavior. Here is a list of common headers that are NOT to be proxied:
The data returned by these composables will be stored inside the page payload. This means that every key returned that is not used in your component will be added to the payload.
Imagine that `/api/mountains/everest` returns the following object:
```json
{
"title": "Mount Everest",
"description": "Mount Everest is Earth's highest mountain above sea level, located in the Mahalangur Himal sub-range of the Himalayas. The China–Nepal border runs across its summit point",
Calling `$fetch` in code that is executed on both server and client (such as in the top level of a `setup` function) will fetch the data twice - initially on the server and then again on the client during the hydration phase. This is because `$fetch` does not automatically serialize or transfer the data to the client.
For example:
**/pages/price.vue**: Isomorphic code below executes `$fetch` twice (initially on the server, then again on the client).
```ts
<scriptsetuplang="ts">
const price = $fetch('/api/price');
</script>
```
**/server/api/product.get.ts**: Server only code below executes `$fetch` only once at the server side.
If fetching twice isn't your intended behavior, to fetch only on the server side and transfer it to the client, wrap `$fetch` with `useAsyncData()` or use `useFetch()`.
```ts
<scriptsetuplang="ts">
const { data } = await useAsyncData('price', () => $fetch('/api/price'));
If you are using `async setup()`, the current component instance will be lost after the first `await`. (This is a Vue 3 limitation.) If you want to use multiple async operations, such as multiple calls to `useFetch`, you will need to use `<script setup>`, await them together at the end of setup, or alternatively use `defineNuxtComponent` (which applies a custom transform to the setup function).
Using `<script setup>` is recommended, as it removes the limitation of using top-level await. [Read more](https://vuejs.org/api/sfc-script-setup.html#top-level-await)
When fetching data from the `server` directory, the response is serialized using `JSON.stringify`. However, since serialization is limited to only JavaScript primitive types, Nuxt does its best to convert the return type of `$fetch` and `useFetch` to match the actual value.
::alert{icon=👉}
You can learn more about `JSON.stringify` limitations [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#description).
::
### Example
```ts [server/api/foo.ts]
export default defineEventHandler(() => {
return new Date()
})
```
```vue [app.vue]
<scriptsetuplang="ts">
// Type of `data` is inferred as string even though we returned a Date object
const { data } = await useFetch('/api/foo')
</script>
```
### Custom serializer function
To customize the serialization behavior, you can define a `toJSON` function on your returned object. If you define a `toJSON` method, Nuxt will respect the return type of the function and will not try to convert the types.
```ts [server/api/bar.ts]
export default defineEventHandler(() => {
const data = {
createdAt: new Date(),
toJSON() {
return {
createdAt: {
year: this.createdAt.getFullYear(),
month: this.createdAt.getMonth(),
day: this.createdAt.getDate(),
},
}
},
}
return data
})
```
```vue [app.vue]
<scriptsetuplang="ts">
// Type of `data` is inferred as
// {
// createdAt: {
// year: number
// month: number
// day: number
// }
// }
const { data } = await useFetch('/api/bar')
</script>
```
### Using an alternative serializer
Nuxt does not currently support an alternative serializer to `JSON.stringify`. However, you can return your payload as a normal string and utilize the `toJSON` method to maintain type safety.