From 06e1676ed187788b40c9029dfbdc23c0618564c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Chopin?= Date: Thu, 16 May 2024 13:43:11 +0200 Subject: [PATCH] docs: add custom `useFetch` recipe (#27208) --- docs/2.guide/0.index.md | 3 + docs/2.guide/4.recipes/3.custom-usefetch.md | 105 ++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 docs/2.guide/4.recipes/3.custom-usefetch.md diff --git a/docs/2.guide/0.index.md b/docs/2.guide/0.index.md index b45915e129..a93d01cdb6 100644 --- a/docs/2.guide/0.index.md +++ b/docs/2.guide/0.index.md @@ -16,4 +16,7 @@ surround: false ::card{icon="i-ph-star-duotone" title="Going Further" to="/docs/guide/going-further"} Master Nuxt with advanced concepts like experimental features, hooks, modules, and more. :: + ::card{icon="i-ph-book-open-duotone" title="Recipes" to="/docs/guide/recipes"} + Find solutions to common problems and learn how to implement them in your Nuxt project. + :: :: diff --git a/docs/2.guide/4.recipes/3.custom-usefetch.md b/docs/2.guide/4.recipes/3.custom-usefetch.md new file mode 100644 index 0000000000..e27af2712f --- /dev/null +++ b/docs/2.guide/4.recipes/3.custom-usefetch.md @@ -0,0 +1,105 @@ +--- +navigation.title: 'Custom useFetch' +title: Custom useFetch in Nuxt +description: 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`](/docs/api/utils/dollarfetch) utility function (used by the [`useFetch`](/docs/api/composables/use-fetch) 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](/docs/guide/directory-structure/plugins). + +::note +`$fetch` is a configured instance of [ofetch](https://github.com/unjs/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](https://github.com/atinux/nuxt-auth-utils) +- If the API responds with a `401` status code, we redirect the user to the `/login` page + +```ts [plugins/api.ts] +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: + +```vue [app.vue] + +``` + +::callout +Wrapping with [`useAsyncData`](/docs/api/composables/use-async-data) **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`: + +```ts [composables/useAPI.ts] +import type { UseFetchOptions } from 'nuxt/app' + +export function useAPI( + url: string | (() => string), + options: Omit, 'default'> & { default: () => T | Ref }, +) { + return useFetch(url, { + ...options, + $fetch: useNuxtApp().$api + }) +} +``` + +Let's use the new composable and have a nice and clean component: + +```vue [app.vue] + +``` + +::callout{icon="i-simple-icons-youtube" color="red" to="https://www.youtube.com/watch?v=jXH8Tr-exhI"} +Watch a video about custom `$fetch` and Repository Pattern in Nuxt. +:: + +::note +We are currently discussing to find a cleaner way to let you create a custom fetcher, see https://github.com/nuxt/nuxt/issues/14736. +::