Custom useFetch in Nuxt
How to create a custom fetcher for calling your external API in Nuxt 3.
When working with Nuxt, most of the time your are making the frontend and fetching an API made with another language (ex: PHP with Laravel).
When we created the useFetch
composable and $fetch
utility function, we made them immutable on purpose to avoid any side-effects. This is important to keep a consistent behaviour through your application and avoid any modules to break your API calls, for example.
Instead, we prefer that you create a custom fetcher for your API, which could also be useful if you have multiple APIs to call.
Custom $fetch
First, we need to create a Nuxt plugin to create a custom $fetch
instance.
$fetch
is a configured instance of ofetch which support adding the base URL of your Nuxt server as well a direct function calls during SSR (avoiding HTTP roundtrips).I am pretending here that:
- The main API is https://api.nuxt.com
- I am storing the JWT token in a session with nuxt-auth-utils
- If the API responds with a
401
status code, I 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) {
// Add Authorization header
options.headers = options.headers || {}
options.headers.Authorization = `Bearer ${session.value?.token}`
}
},
onResponseError({ response }) {
if (response.status === 401) {
return navigateTo('/login')
}
}
})
// Expose to useNuxtApp().$api
return {
provide: {
api: $api
}
}
})
With this, I am able to use $api
to make API calls directly in my Vue components:
<script setup>
const { $api } = useNuxtApp()
const { data: modules } = await useAsyncData('modules', () => $api('/modules'))
</script>
useAsyncData
here in order to avoid double data fetching when doing server-side rendering (server & client on hydration).Custom useFetch
Now that I have $api
with the logic I want, I can simplify my useAsyncData
usage by creating a useAPI
composable:
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,
})
}
Now I can use my new composable and have a nice and clean component:
<script setup>
const { data: modules } = await useAPI('/modules')
</script>
Notes
We are currently discussing with the core team to find a cleaner way to let you create a custom fetcher, stay tuned on Twitter / X.
Happy Nuxting 🚀