@nuxt/docs-nightly 4.4.0-29554618.f8e243a5 → 4.4.0-29555069.df7ef5d2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -10,19 +10,46 @@ The [`$fetch`](/docs/4.x/api/utils/dollarfetch) utility function (used by the [`
|
|
|
10
10
|
|
|
11
11
|
However, Nuxt provides a way to create a custom fetcher for your API (or multiple fetchers if you have multiple APIs to call).
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Recipe: API Client with Auth
|
|
14
14
|
|
|
15
|
-
Let's
|
|
15
|
+
Let's say you have an external API at `https://api.nuxt.com` that requires JWT authentication via [nuxt-auth-utils](https://github.com/atinux/nuxt-auth-utils), and you want to redirect to `/login` on `401` responses.
|
|
16
|
+
|
|
17
|
+
```ts [app/composables/useAPI.ts]
|
|
18
|
+
export const useAPI = createUseFetch({
|
|
19
|
+
baseURL: 'https://api.nuxt.com',
|
|
20
|
+
onRequest ({ options }) {
|
|
21
|
+
const { session } = useUserSession()
|
|
22
|
+
if (session.value?.token) {
|
|
23
|
+
options.headers.set('Authorization', `Bearer ${session.value.token}`)
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
async onResponseError ({ response }) {
|
|
27
|
+
if (response.status === 401) {
|
|
28
|
+
await navigateTo('/login')
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
})
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Now every call to `useAPI` automatically includes the auth header and handles 401 redirects:
|
|
35
|
+
|
|
36
|
+
```vue [app/pages/dashboard.vue]
|
|
37
|
+
<script setup lang="ts">
|
|
38
|
+
const { data: profile } = await useAPI('/me')
|
|
39
|
+
const { data: orders } = await useAPI('/orders')
|
|
40
|
+
</script>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
:read-more{to="/docs/4.x/api/composables/create-use-fetch"}
|
|
44
|
+
|
|
45
|
+
## Recipe: Custom `$fetch` Instance
|
|
46
|
+
|
|
47
|
+
If you need lower-level control, you can create a custom `$fetch` instance with a [Nuxt plugin](/docs/4.x/directory-structure/app/plugins) and either use it with `useAsyncData` directly or pass it to `createUseFetch`.
|
|
16
48
|
|
|
17
49
|
::note
|
|
18
50
|
`$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).
|
|
19
51
|
::
|
|
20
52
|
|
|
21
|
-
Let's pretend here that:
|
|
22
|
-
- The main API is https://api.nuxt.com
|
|
23
|
-
- We are storing the JWT token in a session with [nuxt-auth-utils](https://github.com/atinux/nuxt-auth-utils)
|
|
24
|
-
- If the API responds with a `401` status code, we redirect the user to the `/login` page
|
|
25
|
-
|
|
26
53
|
```ts [app/plugins/api.ts]
|
|
27
54
|
export default defineNuxtPlugin((nuxtApp) => {
|
|
28
55
|
const { session } = useUserSession()
|
|
@@ -31,7 +58,6 @@ export default defineNuxtPlugin((nuxtApp) => {
|
|
|
31
58
|
baseURL: 'https://api.nuxt.com',
|
|
32
59
|
onRequest ({ request, options, error }) {
|
|
33
60
|
if (session.value?.token) {
|
|
34
|
-
// note that this relies on ofetch >= 1.4.0 - you may need to refresh your lockfile
|
|
35
61
|
options.headers.set('Authorization', `Bearer ${session.value?.token}`)
|
|
36
62
|
}
|
|
37
63
|
},
|
|
@@ -42,7 +68,6 @@ export default defineNuxtPlugin((nuxtApp) => {
|
|
|
42
68
|
},
|
|
43
69
|
})
|
|
44
70
|
|
|
45
|
-
// Expose to useNuxtApp().$api
|
|
46
71
|
return {
|
|
47
72
|
provide: {
|
|
48
73
|
api,
|
|
@@ -51,7 +76,7 @@ export default defineNuxtPlugin((nuxtApp) => {
|
|
|
51
76
|
})
|
|
52
77
|
```
|
|
53
78
|
|
|
54
|
-
|
|
79
|
+
You can then use the custom `$fetch` instance directly:
|
|
55
80
|
|
|
56
81
|
```vue [app/app.vue]
|
|
57
82
|
<script setup>
|
|
@@ -61,65 +86,9 @@ const { data: modules } = await useAsyncData('modules', () => $api('/modules'))
|
|
|
61
86
|
```
|
|
62
87
|
|
|
63
88
|
::callout
|
|
64
|
-
Wrapping with [`useAsyncData`](/docs/4.x/api/composables/use-async-data) **
|
|
65
|
-
::
|
|
66
|
-
|
|
67
|
-
## Custom `useFetch`/`useAsyncData`
|
|
68
|
-
|
|
69
|
-
Now that `$api` has the logic we want, let's create a `useAPI` composable to replace the usage of `useAsyncData` + `$api`:
|
|
70
|
-
|
|
71
|
-
```ts [app/composables/useAPI.ts]
|
|
72
|
-
import type { UseFetchOptions } from 'nuxt/app'
|
|
73
|
-
|
|
74
|
-
export function useAPI<T> (
|
|
75
|
-
url: string | (() => string),
|
|
76
|
-
options?: UseFetchOptions<T>,
|
|
77
|
-
) {
|
|
78
|
-
return useFetch(url, {
|
|
79
|
-
...options,
|
|
80
|
-
$fetch: useNuxtApp().$api as typeof $fetch,
|
|
81
|
-
})
|
|
82
|
-
}
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
Let's use the new composable and have a nice and clean component:
|
|
86
|
-
|
|
87
|
-
```vue [app/app.vue]
|
|
88
|
-
<script setup>
|
|
89
|
-
const { data: modules } = await useAPI('/modules')
|
|
90
|
-
</script>
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
If you want to customize the type of any error returned, you can also do so:
|
|
94
|
-
|
|
95
|
-
```ts
|
|
96
|
-
import type { FetchError } from 'ofetch'
|
|
97
|
-
import type { UseFetchOptions } from 'nuxt/app'
|
|
98
|
-
|
|
99
|
-
interface CustomError {
|
|
100
|
-
message: string
|
|
101
|
-
status: number
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export function useAPI<T> (
|
|
105
|
-
url: string | (() => string),
|
|
106
|
-
options?: UseFetchOptions<T>,
|
|
107
|
-
) {
|
|
108
|
-
return useFetch<T, FetchError<CustomError>>(url, {
|
|
109
|
-
...options,
|
|
110
|
-
$fetch: useNuxtApp().$api,
|
|
111
|
-
})
|
|
112
|
-
}
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
::note
|
|
116
|
-
This example demonstrates how to use a custom `useFetch`, but the same structure is identical for a custom `useAsyncData`.
|
|
89
|
+
Wrapping with [`useAsyncData`](/docs/4.x/api/composables/use-async-data) **avoids double data fetching when doing server-side rendering** (server & client on hydration).
|
|
117
90
|
::
|
|
118
91
|
|
|
119
92
|
:link-example{to="/docs/4.x/examples/advanced/use-custom-fetch-composable"}
|
|
120
93
|
|
|
121
94
|
:video-accordion{title="Watch a video about custom $fetch and Repository Pattern in Nuxt" videoId="jXH8Tr-exhI"}
|
|
122
|
-
|
|
123
|
-
::note
|
|
124
|
-
We are currently discussing to find a cleaner way to let you create a custom fetcher, see https://github.com/nuxt/nuxt/issues/14736.
|
|
125
|
-
::
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: 'createUseAsyncData'
|
|
3
|
+
description: A factory function to create a custom useAsyncData composable with pre-defined default options.
|
|
4
|
+
links:
|
|
5
|
+
- label: Source
|
|
6
|
+
icon: i-simple-icons-github
|
|
7
|
+
to: https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/asyncData.ts
|
|
8
|
+
size: xs
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
`createUseAsyncData` creates a custom [`useAsyncData`](/docs/4.x/api/composables/use-async-data) composable with pre-defined options. The resulting composable is fully typed and works exactly like `useAsyncData`, but with your defaults baked in.
|
|
12
|
+
|
|
13
|
+
::note
|
|
14
|
+
`createUseAsyncData` is a compiler macro. It must be used as an **exported** declaration in the `composables/` directory (or any directory scanned by the Nuxt compiler). Nuxt automatically injects de-duplication keys at build time.
|
|
15
|
+
::
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```ts [app/composables/useCachedData.ts]
|
|
20
|
+
export const useCachedData = createUseAsyncData({
|
|
21
|
+
getCachedData (key, nuxtApp) {
|
|
22
|
+
return nuxtApp.payload.data[key] ?? nuxtApp.static.data[key]
|
|
23
|
+
},
|
|
24
|
+
})
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```vue [app/pages/index.vue]
|
|
28
|
+
<script setup lang="ts">
|
|
29
|
+
const { data: mountains } = await useCachedData(
|
|
30
|
+
'mountains',
|
|
31
|
+
() => $fetch('https://api.nuxtjs.dev/mountains'),
|
|
32
|
+
)
|
|
33
|
+
</script>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The resulting composable has the same signature and return type as [`useAsyncData`](/docs/4.x/api/composables/use-async-data), with all options available for the caller to use or override.
|
|
37
|
+
|
|
38
|
+
## Type
|
|
39
|
+
|
|
40
|
+
```ts [Signature]
|
|
41
|
+
function createUseAsyncData (
|
|
42
|
+
options?: Partial<AsyncDataOptions>,
|
|
43
|
+
): typeof useAsyncData
|
|
44
|
+
|
|
45
|
+
function createUseAsyncData (
|
|
46
|
+
options: (callerOptions: AsyncDataOptions) => Partial<AsyncDataOptions>,
|
|
47
|
+
): typeof useAsyncData
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Options
|
|
51
|
+
|
|
52
|
+
`createUseAsyncData` accepts all the same options as [`useAsyncData`](/docs/4.x/api/composables/use-async-data#params), including `server`, `lazy`, `immediate`, `default`, `transform`, `pick`, `getCachedData`, `deep`, `dedupe`, `timeout`, and `watch`.
|
|
53
|
+
|
|
54
|
+
See the full list of options in the [`useAsyncData` documentation](/docs/4.x/api/composables/use-async-data#params).
|
|
55
|
+
|
|
56
|
+
## Default vs Override Mode
|
|
57
|
+
|
|
58
|
+
### Default Mode (plain object)
|
|
59
|
+
|
|
60
|
+
When you pass a plain object, the factory options act as **defaults**. Callers can override any option:
|
|
61
|
+
|
|
62
|
+
```ts [app/composables/useLazyData.ts]
|
|
63
|
+
export const useLazyData = createUseAsyncData({
|
|
64
|
+
lazy: true,
|
|
65
|
+
server: false,
|
|
66
|
+
})
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
// Uses the defaults (lazy: true, server: false)
|
|
71
|
+
const { data } = await useLazyData('key', () => fetchSomeData())
|
|
72
|
+
|
|
73
|
+
// Caller overrides server to true
|
|
74
|
+
const { data } = await useLazyData('key', () => fetchSomeData(), { server: true })
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Override Mode (function)
|
|
78
|
+
|
|
79
|
+
When you pass a function, the factory options **override** the caller's options. The function receives the caller's options as its argument:
|
|
80
|
+
|
|
81
|
+
```ts [app/composables/useStrictData.ts]
|
|
82
|
+
// deep is always enforced as false
|
|
83
|
+
export const useStrictData = createUseAsyncData(callerOptions => ({
|
|
84
|
+
deep: false,
|
|
85
|
+
}))
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
:read-more{to="/docs/4.x/guide/recipes/custom-usefetch"}
|
|
89
|
+
|
|
90
|
+
:read-more{to="/docs/4.x/api/composables/use-async-data"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: 'createUseFetch'
|
|
3
|
+
description: A factory function to create a custom useFetch composable with pre-defined default options.
|
|
4
|
+
links:
|
|
5
|
+
- label: Source
|
|
6
|
+
icon: i-simple-icons-github
|
|
7
|
+
to: https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/fetch.ts
|
|
8
|
+
size: xs
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
`createUseFetch` creates a custom [`useFetch`](/docs/4.x/api/composables/use-fetch) composable with pre-defined options. The resulting composable is fully typed and works exactly like `useFetch`, but with your defaults baked in.
|
|
12
|
+
|
|
13
|
+
::note
|
|
14
|
+
`createUseFetch` is a compiler macro. It must be used as an **exported** declaration in the `composables/` directory (or any directory scanned by the Nuxt compiler). Nuxt automatically injects de-duplication keys at build time.
|
|
15
|
+
::
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```ts [app/composables/useAPI.ts]
|
|
20
|
+
export const useAPI = createUseFetch({
|
|
21
|
+
baseURL: 'https://api.nuxt.com',
|
|
22
|
+
})
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```vue [app/pages/modules.vue]
|
|
26
|
+
<script setup lang="ts">
|
|
27
|
+
const { data: modules } = await useAPI('/modules')
|
|
28
|
+
</script>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The resulting `useAPI` composable has the same signature and return type as [`useFetch`](/docs/4.x/api/composables/use-fetch), with all options available for the caller to use or override.
|
|
32
|
+
|
|
33
|
+
## Type
|
|
34
|
+
|
|
35
|
+
```ts [Signature]
|
|
36
|
+
function createUseFetch (
|
|
37
|
+
options?: Partial<UseFetchOptions>,
|
|
38
|
+
): typeof useFetch
|
|
39
|
+
|
|
40
|
+
function createUseFetch (
|
|
41
|
+
options: (callerOptions: UseFetchOptions) => Partial<UseFetchOptions>,
|
|
42
|
+
): typeof useFetch
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Options
|
|
46
|
+
|
|
47
|
+
`createUseFetch` accepts all the same options as [`useFetch`](/docs/4.x/api/composables/use-fetch#parameters), including `baseURL`, `headers`, `query`, `onRequest`, `onResponse`, `server`, `lazy`, `transform`, `getCachedData`, and more.
|
|
48
|
+
|
|
49
|
+
See the full list of options in the [`useFetch` documentation](/docs/4.x/api/composables/use-fetch#parameters).
|
|
50
|
+
|
|
51
|
+
## Default vs Override Mode
|
|
52
|
+
|
|
53
|
+
### Default Mode (plain object)
|
|
54
|
+
|
|
55
|
+
When you pass a plain object, the factory options act as **defaults**. Callers can override any option:
|
|
56
|
+
|
|
57
|
+
```ts [app/composables/useAPI.ts]
|
|
58
|
+
export const useAPI = createUseFetch({
|
|
59
|
+
baseURL: 'https://api.nuxt.com',
|
|
60
|
+
lazy: true,
|
|
61
|
+
})
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
// Uses the default baseURL
|
|
66
|
+
const { data } = await useAPI('/modules')
|
|
67
|
+
|
|
68
|
+
// Caller overrides the baseURL
|
|
69
|
+
const { data } = await useAPI('/modules', { baseURL: 'https://other-api.com' })
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Override Mode (function)
|
|
73
|
+
|
|
74
|
+
When you pass a function, the factory options **override** the caller's options. The function receives the caller's options as its argument, so you can read them to compute your overrides:
|
|
75
|
+
|
|
76
|
+
```ts [app/composables/useAPI.ts]
|
|
77
|
+
// baseURL is always enforced, regardless of what the caller passes
|
|
78
|
+
export const useAPI = createUseFetch(callerOptions => ({
|
|
79
|
+
baseURL: 'https://api.nuxt.com',
|
|
80
|
+
}))
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
This is useful for enforcing settings like authentication headers or a specific base URL that should not be changed by the caller.
|
|
84
|
+
|
|
85
|
+
## Combining with a Custom `$fetch`
|
|
86
|
+
|
|
87
|
+
You can pass a custom `$fetch` instance to `createUseFetch`:
|
|
88
|
+
|
|
89
|
+
```ts [app/composables/useAPI.ts]
|
|
90
|
+
export const useAPI = createUseFetch({
|
|
91
|
+
$fetch: useNuxtApp().$api as typeof $fetch,
|
|
92
|
+
})
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
:read-more{to="/docs/4.x/guide/recipes/custom-usefetch"}
|
|
96
|
+
|
|
97
|
+
:read-more{to="/docs/4.x/api/composables/use-fetch"}
|
|
@@ -25,8 +25,8 @@ const { data, status, pending, error, refresh, clear } = await useAsyncData(
|
|
|
25
25
|
</script>
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
::
|
|
29
|
-
|
|
28
|
+
::tip{to="/docs/4.x/guide/recipes/custom-usefetch#custom-usefetch-with-createusefetch"}
|
|
29
|
+
Need a custom `useAsyncData` with pre-defined defaults? Use `createUseAsyncData` to create a fully typed custom composable. See the [custom useFetch recipe](/docs/4.x/guide/recipes/custom-usefetch) for details.
|
|
30
30
|
::
|
|
31
31
|
|
|
32
32
|
::note
|
|
@@ -25,8 +25,8 @@ const { data, status, error, refresh, clear } = await useFetch('/api/modules', {
|
|
|
25
25
|
</script>
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
::
|
|
29
|
-
|
|
28
|
+
::tip{to="/docs/4.x/guide/recipes/custom-usefetch#custom-usefetch-with-createusefetch"}
|
|
29
|
+
Need a custom `useFetch` with pre-defined defaults (like `baseURL` or auth headers)? Use `createUseFetch` to create a fully typed custom composable.
|
|
30
30
|
::
|
|
31
31
|
|
|
32
32
|
::note
|
|
@@ -87,7 +87,7 @@ Keyed state created using `useFetch` can be retrieved across your Nuxt applicati
|
|
|
87
87
|
::
|
|
88
88
|
|
|
89
89
|
::warning
|
|
90
|
-
`useFetch` is a reserved function name transformed by the compiler, so you should not name your own function `useFetch`.
|
|
90
|
+
`useFetch` is a reserved function name transformed by the compiler, so you should not name your own function `useFetch`. To create a custom variant with pre-defined options, use [`createUseFetch`](/docs/4.x/guide/recipes/custom-usefetch#custom-usefetch-with-createusefetch) instead.
|
|
91
91
|
::
|
|
92
92
|
|
|
93
93
|
::warning
|
|
@@ -151,7 +151,6 @@ type UseFetchOptions<DataT> = {
|
|
|
151
151
|
pick?: string[]
|
|
152
152
|
$fetch?: typeof globalThis.$fetch
|
|
153
153
|
watch?: MultiWatchSources | false
|
|
154
|
-
timeout?: MaybeRefOrGetter<number>
|
|
155
154
|
}
|
|
156
155
|
|
|
157
156
|
type AsyncDataRequestContext = {
|
|
@@ -193,7 +192,6 @@ type AsyncDataRequestStatus = 'idle' | 'pending' | 'success' | 'error'
|
|
|
193
192
|
| `body` | `MaybeRefOrGetter<RequestInit['body'] \| Record<string, any>>` | - | Request body. Objects are automatically stringified. |
|
|
194
193
|
| `headers` | `MaybeRefOrGetter<Record<string, string> \| [key, value][] \| Headers>` | - | Request headers. |
|
|
195
194
|
| `baseURL` | `MaybeRefOrGetter<string>` | - | Base URL for the request. |
|
|
196
|
-
| `timeout` | `MaybeRefOrGetter<number>` | - | Timeout in milliseconds to abort the request. |
|
|
197
195
|
| `cache` | `boolean \| string` | - | Cache control. Boolean disables cache, or use Fetch API values: `default`, `no-store`, etc. |
|
|
198
196
|
| `server` | `boolean` | `true` | Whether to fetch on the server. |
|
|
199
197
|
| `lazy` | `boolean` | `false` | If true, resolves after route loads (does not block navigation). |
|