2021-10-11 12:57:54 +00:00
# Data Fetching
2021-11-15 12:09:07 +00:00
Nuxt provides `useFetch` , `useLazyFetch` , `useAsyncData` and `useLazyAsyncData` to handle data fetching within your application.
2021-10-11 12:57:54 +00:00
2021-11-15 13:13:00 +00:00
::alert{icon=👉}
2022-01-11 18:01:53 +00:00
**`useFetch`, `useLazyFetch` , `useAsyncData` and `useLazyAsyncData` only work during `setup` or `Lifecycle Hooks` **
2021-11-15 13:13:00 +00:00
::
2021-10-11 12:57:54 +00:00
## `useAsyncData`
2021-11-15 16:34:39 +00:00
Within your pages, components and plugins you can use `useAsyncData` to get access to data that resolves asynchronously.
2021-10-11 12:57:54 +00:00
### Usage
2022-02-21 11:24:57 +00:00
```ts
2021-11-15 17:04:37 +00:00
const {
data: Ref< DataT > ,
pending: Ref< boolean > ,
refresh: (force?: boolean) => Promise< void > ,
error?: any
} = useAsyncData(
2021-11-11 13:58:08 +00:00
key: string,
2022-02-21 11:24:57 +00:00
handler: (ctx?: NuxtApp) => Promise< Object > ,
2022-03-24 12:13:41 +00:00
options?: {
lazy: boolean,
server: boolean,
watch: WatchSource[]
}
2021-11-11 13:58:08 +00:00
)
2021-10-11 12:57:54 +00:00
```
2022-02-21 11:24:57 +00:00
### Params
2021-10-11 12:57:54 +00:00
* **key**: a unique key to ensure that data fetching can be properly de-duplicated across requests
2022-02-21 11:24:57 +00:00
* **handler**: an asynchronous function that returns a value
2021-10-11 12:57:54 +00:00
* **options**:
2021-11-15 12:09:07 +00:00
* _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` )
2022-02-21 11:24:57 +00:00
* _transform_: a function that can be used to alter `handler` function result after resolving
* _pick_: only pick specified keys in this array from `handler` function result
2022-03-22 18:20:22 +00:00
* _watch_: watch reactive sources to auto refresh
2022-02-21 11:24:57 +00:00
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.
2021-10-11 12:57:54 +00:00
2022-02-21 11:24:57 +00:00
### Return values
2021-11-15 17:04:37 +00:00
* **data**: the result of the asynchronous function that is passed in
* **pending**: a boolean indicating whether the data is still being fetched
2022-02-21 11:24:57 +00:00
* **refresh**: a function that can be used to refresh the data returned by the `handler` function
2021-11-15 17:04:37 +00:00
* **error**: an error object if the data fetching failed
2022-02-21 11:24:57 +00:00
By default, Nuxt waits until a `refresh` is finished before it can be executed again. Passing `true` as parameter skips that wait.
2021-10-11 12:57:54 +00:00
### Example
2022-02-21 11:24:57 +00:00
```ts [server/api/count.ts]
2021-10-11 22:36:50 +00:00
let counter = 0
2021-10-12 10:21:00 +00:00
export default () => {
counter++
return JSON.stringify(counter)
}
2021-10-11 22:36:50 +00:00
```
2021-10-12 12:51:41 +00:00
```vue [app.vue]
2021-10-11 12:57:54 +00:00
< script setup >
2021-10-12 10:21:00 +00:00
const { data } = await useAsyncData('count', () => $fetch('/api/count'))
2021-10-11 12:57:54 +00:00
< / script >
< template >
2021-10-12 10:21:00 +00:00
Page visits: {{ data }}
2021-10-11 12:57:54 +00:00
< / template >
```
2021-11-15 12:09:07 +00:00
## `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).
2022-02-28 10:21:06 +00:00
### Example
```vue
< template >
< div >
{{ pending ? 'Loading' : count }}
< / div >
< / template >
< script setup >
const { pending, data: count } = useLazyAsyncData('count', () => $fetch('/api/count'))
watch(count, (newCount) => {
// Because count starts out null, you won't have access
// to its contents immediately, but you can watch it.
})
< / script >
```
2021-10-11 22:36:50 +00:00
## `useFetch`
2021-11-15 16:34:39 +00:00
Within your pages, components and plugins you can use `useFetch` to universally fetch from any URL.
2021-10-11 22:36:50 +00:00
2021-11-21 12:31:44 +00:00
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.
2021-10-11 22:36:50 +00:00
2021-10-29 11:26:01 +00:00
### Usage
2021-10-11 22:36:50 +00:00
```ts
2021-11-15 17:04:37 +00:00
const {
data: Ref< DataT > ,
pending: Ref< boolean > ,
refresh: (force?: boolean) => Promise< void > ,
error?: any
} = useFetch(url: string, options?)
2021-10-11 22:36:50 +00:00
```
Available options:
2021-10-29 11:26:01 +00:00
* `key` : Provide a custom key
* Options from [ohmyfetch ](https://github.com/unjs/ohmyfetch )
* `method` : Request method
* `params` : Query params
2021-12-06 10:16:18 +00:00
* `headers` : Request headers
2021-11-21 12:31:44 +00:00
* `baseURL` : Base URL for the request
2021-10-29 11:26:01 +00:00
* Options from `useAsyncData`
2021-11-15 12:09:07 +00:00
* `lazy`
2021-10-29 11:26:01 +00:00
* `server`
2021-11-15 12:09:07 +00:00
* `default`
2021-10-29 11:26:01 +00:00
* `pick`
* `transform`
2021-10-11 22:36:50 +00:00
2021-11-15 17:04:37 +00:00
The object returned by `useFetch` has the same properties as that returned by `useAsyncData` ([see above](#useasyncdata)).
2021-10-11 22:36:50 +00:00
### Example
2021-10-12 12:51:41 +00:00
```vue [app.vue]
2021-10-11 22:36:50 +00:00
< script setup >
const { data } = await useFetch('/api/count')
< / script >
< template >
Page visits: {{ data.count }}
< / template >
```
2021-11-15 12:09:07 +00:00
## `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).
2022-02-28 10:21:06 +00:00
### Example
```vue
< template >
<!-- you'll need to handle a loading state -->
< div v-if = "pending" >
Loading ...
< / div >
< div v-else >
< div v-for = "post in posts" >
<!-- do something -->
< / div >
< / div >
< / template >
< script setup >
const { pending, data: posts } = useLazyFetch('/api/posts')
watch(posts, (newPosts) => {
// Because posts starts out null, you won't have access
// to its contents immediately, but you can watch it.
})
< / script >
```
2021-12-20 16:27:36 +00:00
## Isomorphic fetch
2022-03-03 19:30:01 +00:00
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.
2021-12-20 16:27:36 +00:00
2022-03-03 19:30:01 +00:00
### Example: Bypass API headers to client
2021-12-20 16:27:36 +00:00
2022-03-03 19:30:01 +00:00
We can use [`useRequestHeaders` ](/docs/usage/ssr ) to access and proxy cookies to the API from server-side.
2021-12-20 16:27:36 +00:00
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.
```vue
< script setup >
const { data } = useFetch('/api/me', {
headers: useRequestHeaders(['cookie'])
})
< / script >
```
::alert{type="warning"}
2022-03-03 19:30:01 +00:00
Be very careful before proxying headers to an external API and just include headers that you need.
2021-12-20 16:27:36 +00:00
Not all headers are safe to be bypassed and might introduce unwanted behavior.
2022-03-03 19:30:01 +00:00
2021-12-20 16:27:36 +00:00
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`
::
2022-03-03 19:30:01 +00:00
### Example: Pass on cookies from server-side API calls on SSR response
If you want to pass on/proxy cookies in the other direction, from an internal request back to the client, you will need to handle this yourself.
```ts [composables/fetch.ts]
export const fetchWithCookie = async (url: string, cookieName: string) => {
const response = await $fetch.raw(url)
if (process.server) {
const cookies = Object.fromEntries(
response.headers.get('set-cookie')?.split(',').map((a) => a.split('='))
)
if (cookieName in cookies) {
useCookie(cookieName).value = cookies[cookieName]
}
}
return response._data
}
```
```vue
< script setup lang = "ts" >
// This composable will automatically pass on a cookie of our choice.
const result = await fetchWithCookie("/api/with-cookie", "test")
onMounted(() => console.log(document.cookie))
< / script >
```
2021-11-15 12:09:07 +00:00
## Best practices
2021-10-11 12:57:54 +00:00
2021-11-15 12:09:07 +00:00
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.
2021-10-11 12:57:54 +00:00
2021-10-27 12:46:52 +00:00
::alert{icon=👉}
**We strongly recommend you only select the keys that you will use in your component.**
::
2021-10-11 12:57:54 +00:00
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",
"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"
}
```
2021-10-11 22:36:50 +00:00
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:
2021-10-11 12:57:54 +00:00
```vue
< script setup >
2021-10-11 22:36:50 +00:00
const { data: mountain } = await useFetch('/api/mountains/everest', { pick: ['title', 'description'] })
2021-10-11 12:57:54 +00:00
< / script >
< template >
< h1 > {{ mountain.title }}< / h1 >
< p > {{ mountain.description }}< / p >
< / template >
```
2021-10-27 12:46:52 +00:00
2021-11-15 12:09:07 +00:00
## Using async setup
2021-10-27 12:46:52 +00:00
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=👉}
2022-02-07 10:16:45 +00:00
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 )
2021-10-27 12:46:52 +00:00
::
```vue
< script >
export default defineComponent({
async setup() {
const [{ data: organization }, { data: repos }] = await Promise.all([
2021-11-02 09:36:01 +00:00
useFetch(`https://api.github.com/orgs/nuxt`),
useFetch(`https://api.github.com/orgs/nuxt/repos`)
2021-10-27 12:46:52 +00:00
])
return {
organization,
repos
}
}
})
2021-11-02 09:36:01 +00:00
< / script >
2021-10-27 12:46:52 +00:00
< template >
2021-11-02 09:36:01 +00:00
< header >
< h1 > {{ organization.login }}< / h1 >
< p > {{ organization.description }}< / p >
< / header >
2021-10-27 12:46:52 +00:00
< / template >
```