0
# Data Fetching
1
2
Comprehensive data fetching system with server-side rendering support, caching, loading states, and error handling. Nuxt provides powerful composables for fetching data with automatic SSR hydration, request deduplication, and reactive updates.
3
4
> **Note**: Examples use `$fetch`, which is Nuxt's built-in fetch utility available globally in Nuxt applications. For external usage, import it from 'ofetch'.
5
6
## Capabilities
7
8
### Async Data
9
10
Handle asynchronous data fetching with caching, loading states, and error handling.
11
12
```typescript { .api }
13
/**
14
* Handle asynchronous data fetching with caching and loading states
15
* @param key - Unique key for caching the data
16
* @param handler - Function that returns a Promise resolving to the data
17
* @param options - Configuration options for the async data
18
* @returns AsyncData object with data, pending, error, and refresh properties
19
*/
20
function useAsyncData<DataT, ErrorT = Error>(
21
key: string,
22
handler: () => Promise<DataT>,
23
options?: AsyncDataOptions<DataT>
24
): AsyncData<DataT, ErrorT>;
25
26
/**
27
* Lazy version of useAsyncData that doesn't block navigation
28
* @param key - Unique key for caching the data
29
* @param handler - Function that returns a Promise resolving to the data
30
* @param options - Configuration options for the async data
31
* @returns AsyncData object with data, pending, error, and refresh properties
32
*/
33
function useLazyAsyncData<DataT, ErrorT = Error>(
34
key: string,
35
handler: () => Promise<DataT>,
36
options?: AsyncDataOptions<DataT>
37
): AsyncData<DataT, ErrorT>;
38
39
interface AsyncData<DataT, ErrorT> {
40
/** The fetched data */
41
data: Ref<DataT | null>;
42
/** Whether the data is being fetched */
43
pending: Ref<boolean>;
44
/** Any error that occurred during fetching */
45
error: Ref<ErrorT | null>;
46
/** Function to refresh the data */
47
refresh: () => Promise<void>;
48
/** Function to execute the handler */
49
execute: () => Promise<void>;
50
/** Current request status */
51
status: Ref<AsyncDataRequestStatus>;
52
}
53
54
interface AsyncDataOptions<ResT, DataT = ResT, PickKeys extends KeysOf<DataT> = KeysOf<DataT>, DefaultT = null> {
55
/** Whether to fetch on server-side */
56
server?: boolean;
57
/** Whether to fetch on client-side */
58
client?: boolean;
59
/** Whether to use lazy loading (non-blocking) */
60
lazy?: boolean;
61
/** Whether to fetch immediately */
62
immediate?: boolean;
63
/** Default value factory */
64
default?: () => DefaultT | Ref<DefaultT>;
65
/** Transform function for the data */
66
transform?: (input: ResT) => DataT;
67
/** Keys to pick from the result */
68
pick?: PickKeys[];
69
/** Reactive sources to watch for changes */
70
watch?: MultiWatchSources;
71
/** Whether to perform deep watching */
72
deep?: boolean;
73
/** Whether to dedupe identical requests */
74
dedupe?: "cancel" | "defer";
75
}
76
77
type AsyncDataRequestStatus = "idle" | "pending" | "success" | "error";
78
```
79
80
**Usage Examples:**
81
82
```typescript
83
// Basic async data
84
const { data: users, pending, error } = await useAsyncData("users", () =>
85
$fetch("/api/users")
86
);
87
88
// With options
89
const { data: posts, refresh } = await useAsyncData(
90
"posts",
91
() => $fetch("/api/posts"),
92
{
93
server: true,
94
client: false,
95
default: () => [],
96
transform: (data: any[]) => data.slice(0, 10)
97
}
98
);
99
100
// Lazy loading (non-blocking)
101
const { data: comments } = await useLazyAsyncData(
102
"comments",
103
() => $fetch(`/api/posts/${route.params.id}/comments`)
104
);
105
106
// With reactive dependencies
107
const route = useRoute();
108
const { data: product } = await useAsyncData(
109
`product-${route.params.id}`,
110
() => $fetch(`/api/products/${route.params.id}`),
111
{
112
watch: [() => route.params.id]
113
}
114
);
115
116
// Transform and pick data
117
const { data: userProfile } = await useAsyncData(
118
"profile",
119
() => $fetch("/api/user/profile"),
120
{
121
pick: ["id", "name", "email"],
122
transform: (user: any) => ({
123
...user,
124
displayName: `${user.firstName} ${user.lastName}`
125
})
126
}
127
);
128
```
129
130
### Fetch Utilities
131
132
Fetch data from API endpoints with SSR support and request optimization.
133
134
```typescript { .api }
135
/**
136
* Fetch data from an API endpoint with SSR support
137
* @param request - URL string, Request object, or reactive reference
138
* @param opts - Fetch and async data options
139
* @returns AsyncData with fetched data
140
*/
141
function useFetch<ResT = any, ErrorT = FetchError>(
142
request: string | Request | Ref<string | Request> | (() => string | Request),
143
opts?: UseFetchOptions<ResT>
144
): AsyncData<ResT, ErrorT>;
145
146
/**
147
* Lazy version of useFetch that doesn't block navigation
148
* @param request - URL string, Request object, or reactive reference
149
* @param opts - Fetch and async data options
150
* @returns AsyncData with fetched data
151
*/
152
function useLazyFetch<ResT = any, ErrorT = FetchError>(
153
request: string | Request | Ref<string | Request> | (() => string | Request),
154
opts?: UseFetchOptions<ResT>
155
): AsyncData<ResT, ErrorT>;
156
157
interface UseFetchOptions<ResT = any, DataT = ResT, PickKeys extends KeysOf<DataT> = KeysOf<DataT>, DefaultT = null, R extends NitroFetchRequest = string, M extends AvailableRouterMethod<R> = AvailableRouterMethod<R>> extends AsyncDataOptions<ResT, DataT, PickKeys, DefaultT>, FetchOptions<R, M> {
158
/** Request method */
159
method?: M;
160
/** Request body */
161
body?: RequestInit["body"] | Record<string, any>;
162
/** Request headers */
163
headers?: Record<string, string> | [key: string, value: string][] | Headers;
164
/** Query parameters */
165
query?: SearchParams;
166
/** Request parameters */
167
params?: SearchParams;
168
/** Base URL for the request */
169
baseURL?: string;
170
/** Request timeout in milliseconds */
171
timeout?: number;
172
/** Request retry configuration */
173
retry?: number | false;
174
/** Whether to retry on status codes */
175
retryStatusCodes?: number[];
176
/** Delay between retries */
177
retryDelay?: number;
178
/** Request interceptors */
179
onRequest?: (context: FetchContext) => Promise<void> | void;
180
/** Request error handlers */
181
onRequestError?: (context: FetchContext & { error: FetchError }) => Promise<void> | void;
182
/** Response interceptors */
183
onResponse?: (context: FetchContext & { response: FetchResponse<ResT> }) => Promise<void> | void;
184
/** Response error handlers */
185
onResponseError?: (context: FetchContext & { response: FetchResponse<ResT> }) => Promise<void> | void;
186
}
187
```
188
189
**Usage Examples:**
190
191
```typescript
192
// Basic fetch
193
const { data: users } = await useFetch("/api/users");
194
195
// With method and body
196
const { data: newUser } = await useFetch("/api/users", {
197
method: "POST",
198
body: { name: "John", email: "john@example.com" }
199
});
200
201
// With query parameters
202
const { data: products } = await useFetch("/api/products", {
203
query: { category: "electronics", limit: 20 }
204
});
205
206
// Reactive URL
207
const route = useRoute();
208
const { data: user } = await useFetch(() => `/api/users/${route.params.id}`);
209
210
// With headers and interceptors
211
const { data, error } = await useFetch("/api/protected", {
212
headers: {
213
Authorization: `Bearer ${token}`
214
},
215
onRequest({ request, options }) {
216
console.log("Making request to:", request);
217
},
218
onResponse({ response }) {
219
console.log("Response status:", response.status);
220
},
221
onResponseError({ response }) {
222
console.error("Request failed:", response.status);
223
}
224
});
225
226
// Lazy fetch with transform
227
const { data: processedData } = await useLazyFetch("/api/raw-data", {
228
transform: (data: any[]) => data.map(item => ({
229
id: item.id,
230
title: item.title.toUpperCase(),
231
createdAt: new Date(item.created_at)
232
}))
233
});
234
```
235
236
### Data Management
237
238
Manage and refresh cached async data.
239
240
```typescript { .api }
241
/**
242
* Get existing async data by key
243
* @param key - The key used when calling useAsyncData
244
* @returns Object with data reference
245
*/
246
function useNuxtData<DataT = any>(key: string): {
247
data: Ref<DataT | null>;
248
};
249
250
/**
251
* Refresh cached async data by key(s)
252
* @param keys - Optional key or keys to refresh, refreshes all if not provided
253
* @returns Promise resolving when refresh is complete
254
*/
255
function refreshNuxtData(keys?: string | string[]): Promise<void>;
256
257
/**
258
* Clear cached async data
259
* @param keys - Keys to clear or filter function
260
*/
261
function clearNuxtData(keys?: string | string[] | ((key: string) => boolean)): void;
262
```
263
264
**Usage Examples:**
265
266
```typescript
267
// Access existing data
268
const { data: users } = useNuxtData("users");
269
270
// Refresh specific data
271
await refreshNuxtData("users");
272
273
// Refresh multiple keys
274
await refreshNuxtData(["users", "posts"]);
275
276
// Refresh all data
277
await refreshNuxtData();
278
279
// Clear specific data
280
clearNuxtData("users");
281
282
// Clear multiple keys
283
clearNuxtData(["users", "posts"]);
284
285
// Clear with filter
286
clearNuxtData((key) => key.startsWith("user-"));
287
288
// Manual refresh with useAsyncData
289
const { data: posts, refresh } = await useAsyncData("posts", () =>
290
$fetch("/api/posts")
291
);
292
293
// Refresh this specific data
294
await refresh();
295
```
296
297
### Advanced Data Fetching Patterns
298
299
```typescript
300
// Dependent data fetching
301
const { data: user } = await useAsyncData("user", () =>
302
$fetch("/api/user")
303
);
304
305
const { data: userPosts } = await useAsyncData(
306
"user-posts",
307
() => $fetch(`/api/users/${user.value?.id}/posts`),
308
{
309
// Only fetch when user is available
310
server: false,
311
watch: [user],
312
immediate: false
313
}
314
);
315
316
// Conditional fetching
317
const route = useRoute();
318
const shouldFetch = computed(() => route.name === "dashboard");
319
320
const { data: analytics } = await useAsyncData(
321
"analytics",
322
() => $fetch("/api/analytics"),
323
{
324
server: shouldFetch.value,
325
client: shouldFetch.value
326
}
327
);
328
329
// Polling data
330
const { data: liveData, refresh } = await useAsyncData(
331
"live-data",
332
() => $fetch("/api/live-data")
333
);
334
335
// Set up polling
336
const { pause, resume } = useIntervalFn(async () => {
337
await refresh();
338
}, 5000);
339
340
// Optimistic updates
341
const { data: todos, refresh } = await useAsyncData("todos", () =>
342
$fetch("/api/todos")
343
);
344
345
async function addTodo(newTodo: Todo) {
346
// Optimistically update
347
todos.value = [...(todos.value || []), { ...newTodo, id: Date.now() }];
348
349
try {
350
await $fetch("/api/todos", {
351
method: "POST",
352
body: newTodo
353
});
354
// Refresh to get server state
355
await refresh();
356
} catch (error) {
357
// Revert on error
358
await refresh();
359
throw error;
360
}
361
}
362
```
363
364
### Error Handling
365
366
```typescript
367
// Basic error handling
368
const { data, error, pending } = await useAsyncData("users", async () => {
369
try {
370
return await $fetch("/api/users");
371
} catch (err) {
372
throw createError({
373
statusCode: 500,
374
statusMessage: "Failed to fetch users"
375
});
376
}
377
});
378
379
// Global error handling
380
const { data: posts } = await useFetch("/api/posts", {
381
onResponseError({ response }) {
382
if (response.status === 401) {
383
throw createError({
384
statusCode: 401,
385
statusMessage: "Unauthorized"
386
});
387
}
388
}
389
});
390
391
// Retry logic
392
const { data: unreliableData } = await useFetch("/api/unreliable", {
393
retry: 3,
394
retryDelay: 1000,
395
retryStatusCodes: [408, 409, 425, 429, 500, 502, 503, 504]
396
});
397
```
398
399
## Types
400
401
```typescript { .api }
402
interface FetchError extends Error {
403
request?: RequestInfo;
404
options?: RequestInit;
405
response?: Response;
406
data?: any;
407
status?: number;
408
statusText?: string;
409
}
410
411
interface FetchContext<T = any, R extends NitroFetchRequest = NitroFetchRequest> {
412
request: R;
413
options: FetchOptions<R>;
414
response?: FetchResponse<T>;
415
error?: FetchError;
416
}
417
418
interface FetchResponse<T> extends Response {
419
_data?: T;
420
}
421
422
type SearchParams = Record<string, any> | [key: string, value: string][] | string | URLSearchParams;
423
424
type MultiWatchSources = (WatchSource<unknown> | object)[];
425
426
type KeysOf<T> = Array<
427
T extends T
428
? keyof T extends string
429
? keyof T
430
: string
431
: never
432
>;
433
434
type AvailableRouterMethod<R extends NitroFetchRequest> =
435
R extends string
436
? "GET" | "HEAD" | "PATCH" | "POST" | "PUT" | "DELETE" | "OPTIONS"
437
: "GET" | "HEAD" | "PATCH" | "POST" | "PUT" | "DELETE" | "OPTIONS";
438
439
type NitroFetchRequest = string | Request;
440
```