@nuxt/docs-nightly 5.0.0-29841017.b1cd49dc → 5.0.0-29841042.d5a56a2a
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.
- package/4.api/2.composables/create-use-async-data.md +45 -2
- package/4.api/2.composables/create-use-fetch.md +45 -3
- package/4.api/2.composables/use-async-data.md +5 -0
- package/4.api/2.composables/use-fetch.md +2 -0
- package/4.api/3.utils/define-use-async-data-addon.md +57 -0
- package/4.api/3.utils/define-use-fetch-addon.md +168 -0
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: 'createUseAsyncData'
|
|
3
3
|
description: A factory function to create a custom useAsyncData composable with pre-defined default options.
|
|
4
|
-
minimalVersion: "4.
|
|
4
|
+
minimalVersion: "4.4"
|
|
5
5
|
links:
|
|
6
6
|
- label: Source
|
|
7
7
|
icon: i-simple-icons-github
|
|
@@ -40,7 +40,7 @@ The resulting composable has the same signature and return type as [`useAsyncDat
|
|
|
40
40
|
|
|
41
41
|
```ts [Signature]
|
|
42
42
|
function createUseAsyncData (
|
|
43
|
-
options?: Partial<AsyncDataOptions
|
|
43
|
+
options?: Partial<AsyncDataOptions> & { addons?: UseAsyncDataAddon[] },
|
|
44
44
|
): typeof useAsyncData
|
|
45
45
|
|
|
46
46
|
function createUseAsyncData (
|
|
@@ -48,6 +48,8 @@ function createUseAsyncData (
|
|
|
48
48
|
): typeof useAsyncData
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
+
The returned composable's signature includes any custom options and return-value extensions contributed by the [addons](#addons).
|
|
52
|
+
|
|
51
53
|
## Options
|
|
52
54
|
|
|
53
55
|
`createUseAsyncData` accepts all the same options as [`useAsyncData`](/docs/api/composables/use-async-data#parameters), including `server`, `lazy`, `immediate`, `default`, `transform`, `pick`, `getCachedData`, `deep`, `dedupe`, `timeout`, and `watch`.
|
|
@@ -86,6 +88,47 @@ export const useStrictData = createUseAsyncData(callerOptions => ({
|
|
|
86
88
|
}))
|
|
87
89
|
```
|
|
88
90
|
|
|
91
|
+
## Addons
|
|
92
|
+
|
|
93
|
+
In addition to `useAsyncData` options, `createUseAsyncData` accepts an `addons` array. Addons are reusable units of behavior defined with [`defineUseAsyncDataAddon`](/docs/api/utils/define-use-async-data-addon). They can declare custom call-site options, run middleware around the handler, extend the returned object, and attach custom logic to the composable.
|
|
94
|
+
|
|
95
|
+
For example, an addon that refreshes the data whenever the window regains focus, gated behind a custom `refreshOnFocus` option, so callers opt in per call:
|
|
96
|
+
|
|
97
|
+
```ts [app/composables/useCustomAsyncData.ts]
|
|
98
|
+
const refreshOnFocus = defineUseAsyncDataAddon({
|
|
99
|
+
// augment the call-site options for the custom useAsyncData instance 👇
|
|
100
|
+
setup: (options: UseAsyncDataAddonOptions<{ refreshOnFocus?: boolean }>) => {
|
|
101
|
+
// 👈 run code *before* creating the `useAsyncData` instance
|
|
102
|
+
if (import.meta.server || !options.refreshOnFocus) { return }
|
|
103
|
+
|
|
104
|
+
return (asyncData) => {
|
|
105
|
+
// 👈 run code *after* creating the `useAsyncData` instance
|
|
106
|
+
const focused = useWindowFocus()
|
|
107
|
+
watch(focused, (focused) => {
|
|
108
|
+
if (focused) { asyncData.refresh() }
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
return { focused } // 👈 extend the returned object with a new property
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
export const useCustomAsyncData = createUseAsyncData({ addons: [refreshOnFocus] })
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
```vue [app/pages/index.vue]
|
|
120
|
+
<script setup lang="ts">
|
|
121
|
+
// `refreshOnFocus` is typed on the created composable
|
|
122
|
+
const { data } = await useCustomAsyncData(
|
|
123
|
+
'mountains',
|
|
124
|
+
() => $fetch('https://api.nuxtjs.dev/mountains'),
|
|
125
|
+
{ refreshOnFocus: true },
|
|
126
|
+
)
|
|
127
|
+
</script>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
:read-more{to="/docs/api/utils/define-use-async-data-addon"}
|
|
131
|
+
|
|
89
132
|
:read-more{to="/docs/guide/recipes/custom-usefetch"}
|
|
90
133
|
|
|
91
134
|
:read-more{to="/docs/api/composables/use-async-data"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: 'createUseFetch'
|
|
3
3
|
description: A factory function to create a custom useFetch composable with pre-defined default options.
|
|
4
|
-
minimalVersion: "4.
|
|
4
|
+
minimalVersion: "4.4"
|
|
5
5
|
links:
|
|
6
6
|
- label: Source
|
|
7
7
|
icon: i-simple-icons-github
|
|
@@ -35,7 +35,7 @@ The resulting `useAPI` composable has the same signature and return type as [`us
|
|
|
35
35
|
|
|
36
36
|
```ts [Signature]
|
|
37
37
|
function createUseFetch (
|
|
38
|
-
options?: Partial<UseFetchOptions
|
|
38
|
+
options?: Partial<UseFetchOptions> & { addons?: UseFetchAddon[] },
|
|
39
39
|
): typeof useFetch
|
|
40
40
|
|
|
41
41
|
function createUseFetch (
|
|
@@ -44,10 +44,12 @@ function createUseFetch (
|
|
|
44
44
|
|
|
45
45
|
// where the client declares the routes it serves
|
|
46
46
|
function createUseFetch<Routes> (
|
|
47
|
-
options: Partial<UseFetchOptions> & { routes: Routes },
|
|
47
|
+
options: Partial<UseFetchOptions> & { routes: Routes, addons?: UseFetchAddon[] },
|
|
48
48
|
): DeclaredUseFetch<Routes>
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
+
The returned composable's signature includes any custom options and return-value extensions contributed by the [addons](#addons).
|
|
52
|
+
|
|
51
53
|
## Options
|
|
52
54
|
|
|
53
55
|
`createUseFetch` accepts all the same options as [`useFetch`](/docs/api/composables/use-fetch#parameters), including `baseURL`, `headers`, `query`, `onRequest`, `onResponse`, `server`, `lazy`, `transform`, `getCachedData`, and more.
|
|
@@ -164,6 +166,46 @@ export const useAPI = createUseFetch(callerOptions => ({
|
|
|
164
166
|
The **function signature** (override mode) is required here so that [`useNuxtApp()`](/docs/api/composables/use-nuxt-app) is called in the setup context (at the composable call site) rather than in the module scope, where no Nuxt instance is available.
|
|
165
167
|
::
|
|
166
168
|
|
|
169
|
+
## Addons
|
|
170
|
+
|
|
171
|
+
In addition to `useFetch` options, `createUseFetch` accepts an `addons` array. Addons are reusable units of behavior defined with [`defineUseFetchAddon`](/docs/api/utils/define-use-fetch-addon). They can declare custom call-site options, run middleware around the handler, extend the returned object, and attach custom logic to the composable.
|
|
172
|
+
|
|
173
|
+
For example, an addon that refreshes the data whenever the window regains focus, gated behind a custom `refreshOnFocus` option, so callers opt in per call:
|
|
174
|
+
|
|
175
|
+
```ts [app/composables/useCustomFetch.ts]
|
|
176
|
+
const refreshOnFocus = defineUseFetchAddon({
|
|
177
|
+
// augment the call-site options for the custom useFetch instance 👇
|
|
178
|
+
setup: (options: UseFetchAddonOptions<{ refreshOnFocus?: boolean }>) => {
|
|
179
|
+
// 👈 run code *before* calling `useAsyncData` in `useFetch`
|
|
180
|
+
if (import.meta.server || !options.refreshOnFocus) { return }
|
|
181
|
+
|
|
182
|
+
return (asyncData) => {
|
|
183
|
+
// 👈 run code *after* calling `useAsyncData` in `useFetch`
|
|
184
|
+
const focused = useWindowFocus()
|
|
185
|
+
watch(focused, (focused) => {
|
|
186
|
+
if (focused) { asyncData.refresh() }
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
return { focused } // 👈 extend the returned object with a new property
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
export const useCustomFetch = createUseFetch({ addons: [refreshOnFocus] })
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
```vue [app/pages/index.vue]
|
|
198
|
+
<script setup lang="ts">
|
|
199
|
+
// `refreshOnFocus` is typed on the created composable
|
|
200
|
+
const { data } = await useCustomFetch(
|
|
201
|
+
'https://api.nuxtjs.dev/mountains',
|
|
202
|
+
{ refreshOnFocus: true },
|
|
203
|
+
)
|
|
204
|
+
</script>
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
:read-more{to="/docs/api/utils/define-use-fetch-addon"}
|
|
208
|
+
|
|
167
209
|
:read-more{to="/docs/guide/recipes/custom-usefetch"}
|
|
168
210
|
|
|
169
211
|
:read-more{to="/docs/api/composables/use-fetch"}
|
|
@@ -168,8 +168,11 @@ type AsyncDataOptions<ResT, DataT = ResT> = {
|
|
|
168
168
|
timeout?: number
|
|
169
169
|
enabled?: MaybeRefOrGetter<boolean>
|
|
170
170
|
serialize?: boolean
|
|
171
|
+
middleware?: AsyncDataMiddleware<ResT>[]
|
|
171
172
|
}
|
|
172
173
|
|
|
174
|
+
type AsyncDataMiddleware<ResT> = (next: () => Promise<ResT>, ctx: { signal: AbortSignal }) => Promise<ResT>
|
|
175
|
+
|
|
173
176
|
type AsyncDataRequestContext = {
|
|
174
177
|
/** The reason for this data request */
|
|
175
178
|
cause: 'initial' | 'refresh:manual' | 'refresh:hook' | 'watch'
|
|
@@ -220,6 +223,7 @@ The `handler` function should be **side-effect free** to ensure predictable beha
|
|
|
220
223
|
| `dedupe` :badge[v3.9]{color="info" size="xs" class="align-middle"} | `'cancel' \| 'defer'` | `'cancel'` | Policy when triggering an execution more than once at a time. |
|
|
221
224
|
| `enabled` :badge[v4.5]{color="info" size="xs" class="align-middle"} | `boolean` | `true` | Barrier that gates whether the `handler` may run. While `false`, every execution is blocked (initial fetch, `execute`/`refresh`, and watch triggers), and switching `true` → `false` cancels any in-flight request without clearing `data`. Re-enabling does not refetch on its own. |
|
|
222
225
|
| `serialize` :badge[v4.6]{color="info" size="xs" class="align-middle"} | `boolean` | `true` | Whether to store resolved data in the Nuxt payload (`__NUXT_DATA__`). When `false`, server-fetched data is kept out of the payload and the client will refetch after hydration if a component renders it. Pair with [lazy hydration](/docs/guide/best-practices/performance#lazy-hydration) to avoid hydration mismatches and unnecessary client fetches. |
|
|
226
|
+
| `middleware` :badge[v4.6]{color="info" size="xs" class="align-middle"} | `AsyncDataMiddleware[]` | - | Functions wrapping the execution of the `handler`. Each receives `next`, which runs the rest of the chain and resolves to the data, and the handler context (`{ signal }`). The first entry is the outermost wrapper. Call `next()` to continue or throw to abort. |
|
|
223
227
|
|
|
224
228
|
::note
|
|
225
229
|
All options can be given a `computed` or `ref` value. These will be watched and new requests made automatically with any new values if they are updated.
|
|
@@ -255,6 +259,7 @@ The following options **must be consistent** across all calls with the same key:
|
|
|
255
259
|
- `pick` array
|
|
256
260
|
- `getCachedData` function
|
|
257
261
|
- `default` value
|
|
262
|
+
- `middleware` array
|
|
258
263
|
|
|
259
264
|
The following options **can differ** without triggering warnings:
|
|
260
265
|
- `server`
|
|
@@ -177,6 +177,7 @@ type UseFetchOptions<ResT, DataT = ResT> = {
|
|
|
177
177
|
timeout?: number
|
|
178
178
|
enabled?: MaybeRefOrGetter<boolean>
|
|
179
179
|
serialize?: boolean
|
|
180
|
+
middleware?: AsyncDataMiddleware<ResT>[]
|
|
180
181
|
default?: () => DataT | Ref<DataT>
|
|
181
182
|
transform?: (input: ResT) => DataT | Promise<DataT>
|
|
182
183
|
pick?: string[]
|
|
@@ -236,6 +237,7 @@ type AsyncDataRequestStatus = 'idle' | 'pending' | 'success' | 'error'
|
|
|
236
237
|
| `dedupe` :badge[v3.9]{color="info" size="xs" class="align-middle"} | `'cancel' \| 'defer'` | `'cancel'` | Avoid fetching same key more than once at a time. |
|
|
237
238
|
| `enabled` :badge[v4.5]{color="info" size="xs" class="align-middle"} | `boolean` | `true` | Barrier that gates whether the request may run. While `false`, every execution is blocked (initial fetch, `execute`/`refresh`, and watch triggers), and switching `true` → `false` cancels any in-flight request without clearing `data`. Re-enabling does not refetch on its own. |
|
|
238
239
|
| `serialize` :badge[v4.6]{color="info" size="xs" class="align-middle"} | `boolean` | `true` | Whether to store resolved data in the Nuxt payload (`__NUXT_DATA__`). When `false`, server-fetched data is kept out of the payload and the client will refetch after hydration if a component renders it. Pair with [lazy hydration](/docs/guide/best-practices/performance#lazy-hydration) to avoid hydration mismatches and unnecessary client fetches. |
|
|
240
|
+
| `middleware` :badge[v4.6]{color="info" size="xs" class="align-middle"} | `AsyncDataMiddleware[]` | - | Functions wrapping the execution of the request. Each receives `next`, which runs the rest of the chain and resolves to the data, and the handler context (`{ signal }`). The first entry is the outermost wrapper. Call `next()` to continue or throw to abort. |
|
|
239
241
|
| `$fetch` :badge[v3.2]{color="info" size="xs" class="align-middle"} | `typeof globalThis.$fetch` | - | Custom $fetch implementation. See [Custom useFetch in Nuxt](/docs/guide/recipes/custom-usefetch) |
|
|
240
242
|
|
|
241
243
|
::note
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: 'defineUseAsyncDataAddon'
|
|
3
|
+
description: Define a reusable addon that extends composables created with createUseAsyncData.
|
|
4
|
+
minimalVersion: "4.6"
|
|
5
|
+
links:
|
|
6
|
+
- label: Source
|
|
7
|
+
icon: i-simple-icons-github
|
|
8
|
+
to: https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/addons.ts
|
|
9
|
+
size: xs
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
`defineUseAsyncDataAddon` defines a reusable extension for `useAsyncData` composables created with [`createUseAsyncData`](/docs/api/composables/create-use-async-data).
|
|
13
|
+
|
|
14
|
+
An addon can declare custom call-site options, wrap the handler with middleware, attach custom reactive logic or extend the returned object.
|
|
15
|
+
|
|
16
|
+
:read-more{to="/docs/api/utils/define-use-fetch-addon" title="defineUseFetchAddon: Addons for useAsyncData work the same way as useFetch addons"}
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
For example, an addon adding a `pollEvery` option that refreshes the data on an interval:
|
|
21
|
+
|
|
22
|
+
```ts [app/composables/usePolledAsyncData.ts]
|
|
23
|
+
const polling = defineUseAsyncDataAddon({
|
|
24
|
+
setup: (options: UseAsyncDataAddonOptions<{ pollEvery?: number }>) => {
|
|
25
|
+
if (import.meta.server || !options.pollEvery) { return }
|
|
26
|
+
return (asyncData) => {
|
|
27
|
+
const interval = setInterval(() => asyncData.refresh(), options.pollEvery)
|
|
28
|
+
onScopeDispose(() => clearInterval(interval))
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
export const usePolledAsyncData = createUseAsyncData({ addons: [polling] })
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```vue [app/pages/index.vue]
|
|
37
|
+
<script setup lang="ts">
|
|
38
|
+
// refreshes the data every 30 seconds
|
|
39
|
+
const { data } = await usePolledAsyncData(
|
|
40
|
+
'mountains',
|
|
41
|
+
() => $fetch('https://api.nuxtjs.dev/mountains'),
|
|
42
|
+
{ pollEvery: 30_000 },
|
|
43
|
+
)
|
|
44
|
+
</script>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
::note
|
|
48
|
+
Addons run in the order of the `addons` array, with middleware from the first addon as the outermost wrapper. Listing the same addon object more than once runs it only once.
|
|
49
|
+
::
|
|
50
|
+
|
|
51
|
+
## Type
|
|
52
|
+
|
|
53
|
+
```ts [Signature]
|
|
54
|
+
function defineUseAsyncDataAddon<Opts extends Record<string, any> = {}, Ext = {}> (addon: {
|
|
55
|
+
setup: (options: UseAsyncDataAddonOptions<Opts>) => ((asyncData: AsyncDataAddonInstance) => Ext | void) | void
|
|
56
|
+
}): UseAsyncDataAddon<Opts, Ext>
|
|
57
|
+
```
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: 'defineUseFetchAddon'
|
|
3
|
+
description: Define a reusable addon that extends composables created with createUseFetch.
|
|
4
|
+
minimalVersion: "4.6"
|
|
5
|
+
links:
|
|
6
|
+
- label: Source
|
|
7
|
+
icon: i-simple-icons-github
|
|
8
|
+
to: https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/addons.ts
|
|
9
|
+
size: xs
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
`defineUseFetchAddon` defines a reusable extension for `useFetch` composables created with [`createUseFetch`](/docs/api/composables/create-use-fetch).
|
|
13
|
+
|
|
14
|
+
An addon can declare custom call-site options, wrap the handler with middleware, attach custom reactive logic or extend the returned object.
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
Pass addons to `createUseFetch` via the `addons` array.
|
|
19
|
+
|
|
20
|
+
This addon attaches custom logic: a watcher that refreshes the data whenever the window regains focus and gates it behind a custom `refreshOnFocus` option:
|
|
21
|
+
|
|
22
|
+
```ts [app/composables/useCustomFetch.ts]
|
|
23
|
+
const refreshOnFocus = defineUseFetchAddon({
|
|
24
|
+
// augment the call-site options for the custom useFetch instance 👇
|
|
25
|
+
setup: (options: UseFetchAddonOptions<{ refreshOnFocus?: boolean }>) => {
|
|
26
|
+
// 👈 run code *before* calling `useAsyncData` in `useFetch`
|
|
27
|
+
if (import.meta.server || !options.refreshOnFocus) { return }
|
|
28
|
+
|
|
29
|
+
return (asyncData) => {
|
|
30
|
+
// 👈 run code *after* calling `useAsyncData` in `useFetch`
|
|
31
|
+
const focused = useWindowFocus()
|
|
32
|
+
watch(focused, (focused) => {
|
|
33
|
+
if (focused) { asyncData.refresh() }
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
return { focused } // 👈 extend the returned object with a new property
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
export const useCustomFetch = createUseFetch({ addons: [refreshOnFocus] })
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```vue [app/pages/index.vue]
|
|
45
|
+
<script setup lang="ts">
|
|
46
|
+
const { data } = await useCustomFetch('/some-endpoint', { refreshOnFocus: true })
|
|
47
|
+
</script>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The addon's `setup` function runs on every call of the composable, and receives the merged options (factory defaults plus caller options).
|
|
51
|
+
|
|
52
|
+
::note
|
|
53
|
+
Addons run in the order of the `addons` array, with middleware from the first addon as the outermost wrapper. Listing the same addon object more than once runs it only once.
|
|
54
|
+
::
|
|
55
|
+
|
|
56
|
+
## Custom Options
|
|
57
|
+
|
|
58
|
+
Declare custom options by annotating the `setup` `options` parameter with `UseFetchAddonOptions<{ ... }>`. The options become part of the created composable's signature, fully typed for callers:
|
|
59
|
+
|
|
60
|
+
```ts [app/composables/useCustomFetch.ts]
|
|
61
|
+
const auth = defineUseFetchAddon({
|
|
62
|
+
setup: (options: UseFetchAddonOptions<{ auth?: MaybeRefOrGetter<boolean> }>) => {
|
|
63
|
+
const { token } = useTokenStore()
|
|
64
|
+
options.auth ??= true
|
|
65
|
+
options.onRequest.push(({ options: fetchOptions }) => {
|
|
66
|
+
if (!toValue(options.auth)) { return }
|
|
67
|
+
fetchOptions.headers.set('Authorization', `Bearer ${token.value}`)
|
|
68
|
+
})
|
|
69
|
+
},
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
export const useCustomFetch = createUseFetch({ addons: [auth] })
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
```vue [app/pages/index.vue]
|
|
76
|
+
<script setup lang="ts">
|
|
77
|
+
// `auth` is typed
|
|
78
|
+
const { data } = await useCustomFetch('/public-endpoint', { auth: false })
|
|
79
|
+
</script>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Custom options stay in the merged options object, so they are also passed to `$fetch` along with the request options, which ignores keys it does not recognise.
|
|
83
|
+
|
|
84
|
+
## Extending the Return Value
|
|
85
|
+
|
|
86
|
+
If `setup` returns a function, that function is called with the async data instance. Any object it returns is merged into the composable's return value:
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
const timestamps = defineUseFetchAddon({
|
|
90
|
+
setup: () => {
|
|
91
|
+
const refreshedAt = ref<Date>()
|
|
92
|
+
|
|
93
|
+
return (asyncData) => {
|
|
94
|
+
watch(asyncData.status, (status) => {
|
|
95
|
+
if (status === 'success') { refreshedAt.value = new Date() }
|
|
96
|
+
}, { immediate: true })
|
|
97
|
+
|
|
98
|
+
return { refreshedAt: readonly(refreshedAt) }
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
export const useCustomFetch = createUseFetch({ addons: [timestamps] })
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
```vue [app/pages/index.vue]
|
|
107
|
+
<script setup lang="ts">
|
|
108
|
+
// `refreshedAt` is typed
|
|
109
|
+
const { data, refreshedAt } = await useCustomFetch('/modules')
|
|
110
|
+
</script>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Middleware
|
|
114
|
+
|
|
115
|
+
Middleware wraps the execution of the request handler. Call `next()` to continue the chain (and get the resolved data), or throw to abort. Middleware from the first addon is the outermost wrapper:
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
const minDuration = defineUseFetchAddon({
|
|
119
|
+
setup: (options: UseFetchAddonOptions<{ minDuration?: number }>) => {
|
|
120
|
+
options.middleware.push(async (next) => {
|
|
121
|
+
const [result] = await Promise.all([
|
|
122
|
+
next(),
|
|
123
|
+
new Promise(resolve => setTimeout(resolve, toValue(options.minDuration) ?? 300)),
|
|
124
|
+
])
|
|
125
|
+
return result
|
|
126
|
+
})
|
|
127
|
+
},
|
|
128
|
+
})
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Contributing to the Auto-Generated Key
|
|
132
|
+
|
|
133
|
+
Custom options are **not** part of the auto-generated key by default. If a custom option affects the response, provide a `key` resolver so calls with different values do not share a cached entry. Return a serializable value, or `undefined` to contribute nothing:
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
const scoped = defineUseFetchAddon({
|
|
137
|
+
setup: (options: UseFetchAddonOptions<{ scope?: string }>) => {
|
|
138
|
+
options.scope ??= 'default'
|
|
139
|
+
// ...
|
|
140
|
+
},
|
|
141
|
+
key: options => toValue(options.scope),
|
|
142
|
+
})
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
::warning
|
|
146
|
+
Declare `setup` before `key`. TypeScript infers the custom options from the `setup` parameter annotation, and a `key` resolver written before a `setup` that returns an extension function is checked first and typed with empty options.
|
|
147
|
+
::
|
|
148
|
+
|
|
149
|
+
## Wrapping `then`, `catch` and `finally`
|
|
150
|
+
|
|
151
|
+
As an advanced escape hatch, the extension object may include `then`, `catch` or `finally` functions. These are not merged into the instance. Instead they wrap the corresponding method of the awaitable promise, receiving the original method as their first argument.
|
|
152
|
+
|
|
153
|
+
::note
|
|
154
|
+
When several addons wrap the same method, the wrappers are composed in the order of the `addons` array: the first addon's wrapper wraps all the following ones, just as its middleware would.
|
|
155
|
+
::
|
|
156
|
+
|
|
157
|
+
## Type
|
|
158
|
+
|
|
159
|
+
```ts [Signature]
|
|
160
|
+
function defineUseFetchAddon<Opts extends Record<string, any> = {}, Ext = {}> (addon: {
|
|
161
|
+
setup: (options: UseFetchAddonOptions<Opts>) => ((asyncData: AsyncDataAddonInstance) => Ext | void) | void
|
|
162
|
+
key?: (options: UseFetchAddonOptions<Opts>) => SerializableValue
|
|
163
|
+
}): UseFetchAddon<Opts, Ext>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
::note
|
|
167
|
+
`middleware`, `onRequest`, `onRequestError`, `onResponse` and `onResponseError` are always normalized to arrays, so interceptors from the caller and from other addons are preserved.
|
|
168
|
+
::
|