or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

app-lifecycle.mdconfiguration.mdcore.mddata-fetching.mdhead.mdindex.mdmodule-dev.mdnavigation.mdperformance.mdssr.mdstate.md

data-fetching.mddocs/

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

```