Nuxt/docs/content/3.docs/1.usage/1.data-fetching.md

5.2 KiB
Raw Blame History

Data Fetching

Nuxt provides useFetch, useLazyFetch, useAsyncData and useLazyAsyncData to handle data fetching within your application.

::alert{icon=👉} useFetch, useLazyFetch, useAsyncData and useLazyAsyncData only works during setup or Lifecycle Hooks ::

useAsyncData

Within your pages and components you can use useAsyncData to get access to data that resolves asynchronously.

Usage

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

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 and components you can use useFetch to get universally fetch from any URL.

This composable provides a convenient wrapper around useAsyncData and $fetch and automatically generates a key based on url and fetch options and infers API response type.

Usage

useFetch(url: string, options?)

Available options:

  • key: Provide a custom key
  • Options from ohmyfetch
    • method: Request method
    • params: Query params
    • baseURL: Base URL for request
  • Options from useAsyncData
    • lazy
    • server
    • default
    • pick
    • transform

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).

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 ChinaNepal 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>