When working with Nuxt, you might be making the frontend and fetching an external API, and you might want to set some default options for fetching from your API.
The $fetch utility function (used by the useFetch composable) is intentionally not globally configurable. This is important so that fetching behavior throughout your application remains consistent, and other integrations (like modules) can rely on the behavior of core utilities like $fetch.
However, Nuxt provides a way to create a custom fetcher for your API (or multiple fetchers if you have multiple APIs to call).
$fetchLet's create a custom $fetch instance with a Nuxt plugin.
$fetch is a configured instance of ofetch which supports adding the base URL of your Nuxt server as well as direct function calls during SSR (avoiding HTTP roundtrips).Let's pretend here that:
401 status code, we redirect the user to the /login pageexport default defineNuxtPlugin((nuxtApp) => {
const { session } = useUserSession()
const api = $fetch.create({
baseURL: 'https://api.nuxt.com',
onRequest ({ request, options, error }) {
if (session.value?.token) {
// note that this relies on ofetch >= 1.4.0 - you may need to refresh your lockfile
options.headers.set('Authorization', `Bearer ${session.value?.token}`)
}
},
async onResponseError ({ response }) {
if (response.status === 401) {
await nuxtApp.runWithContext(() => navigateTo('/login'))
}
},
})
// Expose to useNuxtApp().$api
return {
provide: {
api,
},
}
})
With this Nuxt plugin, $api is exposed from useNuxtApp() to make API calls directly from the Vue components:
<script setup>
const { $api } = useNuxtApp()
const { data: modules } = await useAsyncData('modules', () => $api('/modules'))
</script>
useAsyncDataavoid double data fetching when doing server-side rendering (server & client on hydration).useFetch/useAsyncDataNow that $api has the logic we want, let's create a useAPI composable to replace the usage of useAsyncData + $api:
import type { UseFetchOptions } from 'nuxt/app'
export function useAPI<T> (
url: string | (() => string),
options?: UseFetchOptions<T>,
) {
return useFetch(url, {
...options,
$fetch: useNuxtApp().$api as typeof $fetch,
})
}
Let's use the new composable and have a nice and clean component:
<script setup>
const { data: modules } = await useAPI('/modules')
</script>
If you want to customize the type of any error returned, you can also do so:
import type { FetchError } from 'ofetch'
import type { UseFetchOptions } from 'nuxt/app'
interface CustomError {
message: string
statusCode: number
}
export function useAPI<T> (
url: string | (() => string),
options?: UseFetchOptions<T>,
) {
return useFetch<T, FetchError<CustomError>>(url, {
...options,
$fetch: useNuxtApp().$api,
})
}
useFetch, but the same structure is identical for a custom useAsyncData.