Custom useFetch in Nuxt
How to create a custom fetcher for calling your external API in Nuxt 3.
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).
Custom $fetch
Let'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:
- The main API is https://api.nuxt.com
- We are storing the JWT token in a session with nuxt-auth-utils
- If the API responds with a
401
status code, we redirect the user to the/login
page
export default defineNuxtPlugin(() => {
const { session } = useUserSession()
const api = $fetch.create({
baseURL: 'https://api.nuxt.com',
onRequest({ request, options, error }) {
if (session.value?.token) {
const headers = options.headers ||= {}
if (Array.isArray(headers)) {
headers.push(['Authorization', `Bearer ${session.value?.token}`])
} else if (headers instanceof Headers) {
headers.set('Authorization', `Bearer ${session.value?.token}`)
} else {
headers.Authorization = `Bearer ${session.value?.token}`
}
}
},
async onResponseError({ response }) {
if (response.status === 401) {
await 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>
useAsyncData
avoid double data fetching when doing server-side rendering (server & client on hydration).Custom useFetch
Now 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: Omit<UseFetchOptions<T>, 'default'> & { default: () => T | Ref<T> },
) {
return useFetch(url, {
...options,
$fetch: useNuxtApp().$api
})
}
Let's use the new composable and have a nice and clean component:
<script setup>
const { data: modules } = await useAPI('/modules')
</script>