7.1 KiB
Data Fetching
Nuxt provides useFetch
, useLazyFetch
, useAsyncData
and useLazyAsyncData
to handle data fetching within your application.
::alert{icon=👉}
useFetch
, useLazyFetch
, useAsyncData
and useLazyAsyncData
only work during setup
or Lifecycle Hooks
::
useAsyncData
Within your pages, components and plugins you can use useAsyncData
to get access to data that resolves asynchronously.
Usage
const {
data: Ref<DataT>,
pending: Ref<boolean>,
refresh: (force?: boolean) => Promise<void>,
error?: any
} = useAsyncData(
key: string,
fn: () => Object,
options?: { lazy: boolean, server: boolean }
)
- key: a unique key to ensure that data fetching can be properly de-duplicated across requests
- fn an asynchronous function that returns a value.
- options:
- lazy: whether to resolve the async function after loading the route, instead of blocking navigation (defaults to
false
) - default: a factory function to set the default value of the data, before the async function resolves - particularly useful with the
lazy: true
option - server: whether to fetch the data on server-side (defaults to
true
) - transform: A function that can be used to alter fn result after resolving
- pick: Only pick specified keys in this array from fn result
- lazy: whether to resolve the async function after loading the route, instead of blocking navigation (defaults to
useAsyncData
returns an object with the following properties:
- data: the result of the asynchronous function that is passed in
- pending: a boolean indicating whether the data is still being fetched
- refresh: a function that can be used to force a refresh of the data
- error: an error object if the data fetching failed
Under the hood, lazy: false
uses <Suspense>
to block the loading of the route before the data has been fetched. Consider using lazy: true
and implementing a loading state instead for a snappier user experience.
Example
let counter = 0
export default () => {
counter++
return JSON.stringify(counter)
}
<script setup>
const { data } = await useAsyncData('count', () => $fetch('/api/count'))
</script>
<template>
Page visits: {{ data }}
</template>
useLazyAsyncData
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).
useFetch
Within your pages, components and plugins you can use useFetch
to universally fetch from any URL.
This composable provides a convenient wrapper around useAsyncData
and $fetch
. It automatically generates a key based on URL and fetch options, as well as infers API response type.
Usage
const {
data: Ref<DataT>,
pending: Ref<boolean>,
refresh: (force?: boolean) => Promise<void>,
error?: any
} = useFetch(url: string, options?)
Available options:
key
: Provide a custom key- Options from ohmyfetch
method
: Request methodparams
: Query paramsheaders
: Request headersbaseURL
: Base URL for the request
- Options from
useAsyncData
lazy
server
default
pick
transform
The object returned by useFetch
has the same properties as that returned by useAsyncData
(see above).
Example
<script setup>
const { data } = await useFetch('/api/count')
</script>
<template>
Page visits: {{ data.count }}
</template>
useLazyFetch
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).
Isomorphic fetch
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 is originating from the server, it doesn't include the user's browser cookies.
We can use useRequestHeaders
to access and proxy cookies to the API from server-side.
Example:
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.
<script setup>
const { data } = useFetch('/api/me', {
headers: useRequestHeaders(['cookie'])
})
</script>
::alert{type="warning"} Be very careful before proxy headers to an external API and 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:
host
,accept
content-length
,content-md5
,content-type
x-forwarded-host
,x-forwarded-port
,x-forwarded-proto
cf-connecting-ip
,cf-ray
::
Best practices
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.
::alert{icon=👉} We strongly recommend you only select the keys that you will use in your component. ::
Imagine that /api/mountains/everest
returns the following object:
{
"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",
"height": "8,848 m",
"countries": [
"China",
"Nepal"
],
"continent": "Asia",
"image": "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f6/Everest_kalapatthar.jpg/600px-Everest_kalapatthar.jpg"
}
If you plan to only use title
and description
in your component, you can select the keys by chaining the result of $fetch
or pick
option:
<script setup>
const { data: mountain } = await useFetch('/api/mountains/everest', { pick: ['title', 'description'] })
</script>
<template>
<h1>{{ mountain.title }}</h1>
<p>{{ mountain.description }}</p>
</template>
Using async setup
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>
or await them together at the end of setup.
::alert{icon=👉}
Using <script setup>
is recommended, as it removes the limitation of using top-level await. Read more
::
<script>
export default defineComponent({
async setup() {
const [{ data: organization }, { data: repos }] = await Promise.all([
useFetch(`https://api.github.com/orgs/nuxt`),
useFetch(`https://api.github.com/orgs/nuxt/repos`)
])
return {
organization,
repos
}
}
})
</script>
<template>
<header>
<h1>{{ organization.login }}</h1>
<p>{{ organization.description }}</p>
</header>
</template>