When it comes to building web applications, efficient data fetching is a crucial aspect. In the world of Vue.js and Nuxt.js, handling data retrieval can be a breeze if you understand the right tools and techniques. In this comprehensive guide, we’ll explore how Nuxt.js simplifies data fetching with its composables, such as useFetch
, useAsyncData
, and $fetch
.
Data Fetching Composables
Nuxt.js provides several composables to streamline data fetching in your Vue components:
useFetch: This composable is the simplest way to fetch data. It’s a wrapper around useAsyncData
and $fetch
, making it ideal for basic data fetching tasks.
<script setup lang="ts">
const { data: count } = await useFetch('/api/count')
</script>
<template>
Page visits: {{ count }}
</template>
$fetch: The $fetch
utility is built on top of the fetch API and offers features like automatic response parsing, error handling, auto-retry, and interceptors.
const users = await $fetch('/api/users').catch((error) => error.data)
useAsyncData: When your data fetching logic becomes more complex or when third-party query layers are involved, useAsyncData
is your go-to choice.
const { data, error } = await useAsyncData('users', () => myGetFunction('users'))
Options for Fine-Tuning
Both useAsyncData
and useFetch
accept a set of common options that allow you to fine-tune their behavior. These options include:
- Lazy Loading: Control whether data fetching should wait for resolution before navigating to a new page. This can be useful for optimizing loading states. You can alternatively use
useLazyFetch
anduseLazyAsyncData
as convenient methods to perform the same.
<script setup lang="ts">
const { pending, data: posts } = useFetch('/api/posts', {
lazy: true
})
</script>
<template>
<!-- you will 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>
const { pending, data: posts } = useLazyFetch('/api/posts')
- Client-Only Fetching: Use the
server
option to specify whether data fetching should occur only on the client-side. This is valuable for non-SEO-sensitive data.
/* This call will only be performed on the client */
const { pending, data: posts } = useFetch('/api/comments', {
lazy: true,
server: false
})
- Minimizing Payload Size: The
pick
option lets you select specific fields from the fetched data to minimize payload size. If you need more control or map over several objects, you can use thetransform
function to alter the result of the query.
<script setup lang="ts">
/* only pick the fields used in your template */
const { data: mountain } = await useFetch('/api/mountains/everest', { pick: ['title', 'description'] })
</script>
<template>
<h1>{{ mountain.title }}</h1>
<p>{{ mountain.description }}</p>
</template>
const { data: mountains } = await useFetch('/api/mountains', {
transform: (mountains) => {
return mountains.map(mountain => ({ title: mountain.title, description: mountain.description }))
}
})
- Caching and Refetching: Prevent duplicate network calls by using keys. You can also manually execute or refresh data using the
execute
orrefresh
functions.
<script setup lang="ts">
const { data, error, execute, refresh } = await useFetch('/api/users')
</script>
<template>
<div>
<p>{{ data }}</p>
<button @click="refresh">Refresh data</button>
</div>
</template>
- Watch: Re-run data fetching when reactive values change with the
watch
option.
const { data, error, refresh } = await useFetch('/api/users', {
/* Changing the id will trigger a refetch */
watch: [id]
})
const id = ref(1)
- Passing Headers and Cookies: Nuxt.js provides ways to handle headers and cookies during data fetching to ensure proper communication with APIs.
Pass Client Headers to the API
<script setup lang="ts">
const headers = useRequestHeaders(['cookie'])
const { data } = await useFetch('/api/me', { headers })
</script>
Pass Cookies From Server-side API Calls on SSR Response
import { appendResponseHeader, H3Event } from 'h3'
export const fetchWithCookie = async (event: H3Event, url: string) => {
const res = await $fetch.raw(url)
const cookies = (res.headers.get('set-cookie') || '').split(',')
for (const cookie of cookies) {
appendResponseHeader(event, 'set-cookie', cookie)
}
return res._data
}
<script setup lang="ts">
// This composable will automatically pass cookies to the client
const event = useRequestEvent()
const result = await fetchWithCookie(event, '/api/with-cookie')
onMounted(() => console.log(document.cookie))
</script>
Serialization and Type Handling
Nuxt.js takes care of serializing data fetched from the server using JSON.stringify
. However, it attempts to convert the return type of $fetch
and useFetch
to match the actual value.
Customizing serialization behavior is possible by defining a toJSON
function on the returned object. This ensures that Nuxt respects the return type and doesn’t attempt type conversion.
For more advanced scenarios, you can use alternative serializers like superjson
to maintain type safety.
Options API Support
Nuxt 3 provides support for performing asyncData fetching within the Options API. To achieve this, wrap your component definition within defineNuxtComponent
.
<script>
export default defineNuxtComponent({
fetchKey: 'hello',
async asyncData() {
return {
hello: await $fetch('/api/hello')
};
}
});
</script>
Mastering data fetching in Nuxt.js is a crucial skill for building efficient and responsive web applications. By utilizing composables like useFetch
, useAsyncData
, and $fetch
, along with their respective options, you can streamline the process and ensure smooth data retrieval.
Whether you’re working with basic data fetching or dealing with more complex scenarios, Nuxt.js offers the tools and flexibility to meet your needs. So, go ahead, dive into data fetching with Nuxt.js, and supercharge your Vue.js applications!