mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-11 08:33:53 +00:00
docs: add custom useFetch
recipe (#27208)
This commit is contained in:
parent
3dbe7ce348
commit
06e1676ed1
@ -16,4 +16,7 @@ surround: false
|
|||||||
::card{icon="i-ph-star-duotone" title="Going Further" to="/docs/guide/going-further"}
|
::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.
|
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.
|
||||||
|
::
|
||||||
::
|
::
|
||||||
|
105
docs/2.guide/4.recipes/3.custom-usefetch.md
Normal file
105
docs/2.guide/4.recipes/3.custom-usefetch.md
Normal file
@ -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]
|
||||||
|
<script setup>
|
||||||
|
const { $api } = useNuxtApp()
|
||||||
|
const { data: modules } = await useAsyncData('modules', () => $api('/modules'))
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
::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<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:
|
||||||
|
|
||||||
|
```vue [app.vue]
|
||||||
|
<script setup>
|
||||||
|
const { data: modules } = await useAPI('/modules')
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
::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.
|
||||||
|
::
|
Loading…
Reference in New Issue
Block a user