A Comprehensive Guide to Data Fetching in Nuxt 3
With Nuxt 3's rendering modes, you can execute API calls and render pages both on the client and server, which has some challenges. For example, we want to avoid duplicate network calls, efficient caching, and ensure that the calls work across environments. To address these challenges, Nuxt provides a built-in data fetching library ($fetch
) and two composable (useFetch
and useAsyncData
).
In this article, I'll explain everything you need to know about the different data fetching methods available in Nuxt 3 and when to use them.
Data Fetching Library
Nuxt has a built-in library for data fetching: ofetch
ofetch
is built on top of the fetch API and provides some handy features like:
- Works on node, browser, and workers.
- Smartly parses JSON and native values in the response.
- Automatically throw errors when
response.ok
isfalse
with a friendly error message and compact stack (hiding internals). - Automatically retries the request if an error happens.
- You can provide async interceptors to hook into lifecycle events of
ofetch
calls.
You can use ofetch
in your whole application with the $fetch
alias:
useFetch
useFetch
is the most straightforward way to handle data fetching in a component setup function.
useFetch
returns three reactive variables and a function:
data
: a reactive variable that contains the result of the asynchronous function that is passed in.error
: a reactive error object containing information about the request error.pending
: a reactive boolean indicating whether the request is in progress.refresh/execute
: a function to refresh the data returned by thehandler
function. By default, Nuxt waits until arefresh
is finished before it can be executed again.
The useFetch
composable is responsible for forwarding the data to the client if the API call was executed on the server. This way, the client doesn't need to refetch the same data on the client side when the page hydrates. You can inspect this payload via useNuxtApp.payload()
; the Nuxt DevTools visualize this data in the payload tab.
useFetch
additionally reduces API calls by using a key to cache API responses. The key is automatically generated based on the URL and the fetch options. The useFetch
composable is auto-imported and can be used in setup
functions, lifecycle hooks, and plugin or route middleware.
You can use the value of a ref
in the URL string to ensure your component updates when the reactive variable changes:
If the todoId
value changes, the URL will update accordingly and the data will be fetched again.
If you want, you can check out the official documentation for more information.
Options
useFetch
accepts a set of options as the last argument, which can be used to control the behavior of the composable.
Lazy
Data-fetching composables will automatically wait for the asynchronous function to resolve before navigating to a new page when using Vue's Suspense. Nuxt uses Vue’s <Suspense>
component under the hood to prevent navigation before every async data is available to the view.
However, if you want to bypass this behavior during client-side navigation, you can use the lazy
option:
In such cases, you'll need to handle the loading state manually by using the pending
value.
Alternatively, you have the option of using useLazyFetch
which is a convenient method to achieve the same result:
Client-only
By default, data-fetching composables execute their asynchronous function in both client and server environments. To restrict the execution to the client side only, you can set the server
option to false
:
This can be particularly useful when combined with the lazy
option for data that is not required during the initial rendering, such as non-SEO sensitive data.
Warning
If you have not fetched data on the server, for instance using server: false
, the data will not be fetched until the hydration process is complete.
This implies that even if you await useFetch
on the client side, the data
variable will continue to be null within <script setup>
.
Minimize payload size
The pick
option helps you minimize the payload size stored in your HTML document by selecting the fields you want to be returned from the composables:
To gain more control or iterate over multiple objects, you can utilize the transform
function to modify the query result:
Refetching
To manually fetch or update data, you can employ the execute
or refresh
function offered by the composables:
Both functions serve the same purpose, but execute
is an alias for refresh
and is more semantically suitable when immediate: false
is used. When the immediate
option is set to false
(defaults to true
), it will prevent the request from firing immediately.
Utilize the watch
option to rerun your fetching function whenever other reactive values in your application undergo changes:
When to use refresh vs. a watch option?
Use refresh()
when you are aware that the data on the server side has been modified, and you need to update the data on the client side accordingly.
When the user modifies parameters that need to be sent to the server, set those parameters as a watch source. For instance, if you want to filter API results using a search parameter, watch that parameter. This ensures that whenever users change their query, fresh and accurate data will be reloaded from the API.
Query Search Params
With the use of the query
option, you can include search parameters in your query:
This option is an extension of ofetch and leverages ufo to generate the URL. The objects provided are automatically converted to string format.
Interceptors
You can define async interceptors to hook into lifecycle events of the API call:
These options are provided by the built-in ofetch library.
useAsyncData
useFetch
is designed to fetch data from a given URL, while useAsyncData
allows for more intricate logic. Essentially, useFetch(url)
is almost equivalent to useAsyncData(url, () => $fetch(url))
, providing a more streamlined developer experience for the most common use case.
However, there are situations where employing the useFetch
composable may not be suitable, such as when a CMS or a third-party service offers its own query layer. In such cases, you can utilize useAsyncData
to encapsulate your calls and still enjoy the benefits provided by the composable:
In useAsyncData
, the first argument serves as the unique key for caching the response obtained from the second argument, which is the querying function. However, if you prefer, you can omit this argument and directly pass the querying function itself. In such cases, the unique key will be automatically generated.
Info
Both useAsyncData
and useFetch
provide the same object type as their return value and accept a shared set of options as their last argument. These options allow you to customize the behavior of the composables, including features such as navigation blocking, caching, and execution control.
You can check out the official documentation for more information.
Conclusion
Let's summarize and explain when you should use which data fetching method:
- The
$fetch
function is a suitable choice if you intend to initiate a network request based on user interaction. It is recommended to utilize$fetch
when sending data to an event handler, performing client-side logic exclusively, or in conjunction with theuseAsyncData
composable. - The
useFetch
composable is the simplest approach to handle data fetching within a component's setup function. - If you require more precise control over the data fetching process, you can opt for
useAsyncData
in combination with$fetch
.
If you liked this article, follow me on Twitter to get notified about new blog posts and more content from me.
Alternatively (or additionally), you can subscribe to my weekly Vue newsletter: