or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

browser-integration.mdcache-management.mdclient-management.mdhydration.mdindex.mdinfinite-queries.mdmutations.mdquery-observers.mdquery-operations.mdutilities.md

infinite-queries.mddocs/

0

# Infinite Queries

1

2

Specialized functionality for paginated data with automatic page management, bi-directional fetching, cursor-based pagination, and intelligent loading states.

3

4

## Capabilities

5

6

### InfiniteQueryObserver

7

8

Extended observer for managing paginated queries with automatic page handling.

9

10

```typescript { .api }

11

/**

12

* Observer for infinite/paginated queries

13

* Extends QueryObserver with pagination-specific functionality

14

*/

15

class InfiniteQueryObserver<

16

TData = unknown,

17

TError = Error,

18

TQueryData = TData,

19

TQueryKey extends QueryKey = QueryKey,

20

TPageParam = unknown

21

> extends QueryObserver<InfiniteData<TData>, TError, InfiniteData<TQueryData>, TQueryKey> {

22

constructor(client: QueryClient, options: InfiniteQueryObserverOptions<TData, TError, TQueryData, TQueryKey, TPageParam>);

23

24

/**

25

* Fetch the next page of data

26

* @param options - Options for fetching next page

27

* @returns Promise resolving to updated infinite query result

28

*/

29

fetchNextPage(options?: FetchNextPageOptions): Promise<InfiniteQueryObserverResult<TData, TError>>;

30

31

/**

32

* Fetch the previous page of data

33

* @param options - Options for fetching previous page

34

* @returns Promise resolving to updated infinite query result

35

*/

36

fetchPreviousPage(options?: FetchPreviousPageOptions): Promise<InfiniteQueryObserverResult<TData, TError>>;

37

38

/**

39

* Get current infinite query result

40

* @returns Current infinite query observer result

41

*/

42

getCurrentResult(): InfiniteQueryObserverResult<TData, TError>;

43

}

44

45

interface InfiniteQueryObserverOptions<

46

TData = unknown,

47

TError = Error,

48

TQueryData = TData,

49

TQueryKey extends QueryKey = QueryKey,

50

TPageParam = unknown

51

> extends Omit<QueryObserverOptions<InfiniteData<TData>, TError, InfiniteData<TQueryData>, TQueryKey>, 'queryFn'> {

52

/**

53

* Query function that receives page parameter

54

* @param context - Query function context with pageParam

55

* @returns Promise resolving to page data

56

*/

57

queryFn: (context: QueryFunctionContext<TQueryKey, TPageParam>) => Promise<TQueryData>;

58

59

/**

60

* Function to get the next page parameter

61

* @param lastPage - The last page of data

62

* @param allPages - All pages fetched so far

63

* @param lastPageParam - The last page parameter used

64

* @param allPageParams - All page parameters used so far

65

* @returns Next page parameter or undefined if no more pages

66

*/

67

getNextPageParam: GetNextPageParamFunction<TQueryData, TPageParam>;

68

69

/**

70

* Function to get the previous page parameter

71

* @param firstPage - The first page of data

72

* @param allPages - All pages fetched so far

73

* @param firstPageParam - The first page parameter used

74

* @param allPageParams - All page parameters used so far

75

* @returns Previous page parameter or undefined if no more pages

76

*/

77

getPreviousPageParam?: GetPreviousPageParamFunction<TQueryData, TPageParam>;

78

79

/**

80

* Initial page parameter for the first page

81

*/

82

initialPageParam: TPageParam;

83

84

/**

85

* Maximum number of pages to keep in memory

86

* Older pages are removed when limit is exceeded

87

*/

88

maxPages?: number;

89

}

90

91

interface InfiniteQueryObserverResult<TData = unknown, TError = Error>

92

extends QueryObserverResult<InfiniteData<TData>, TError> {

93

94

/**

95

* Function to fetch the next page

96

* @param options - Fetch options

97

* @returns Promise resolving to updated result

98

*/

99

fetchNextPage: (options?: FetchNextPageOptions) => Promise<InfiniteQueryObserverResult<TData, TError>>;

100

101

/**

102

* Function to fetch the previous page

103

* @param options - Fetch options

104

* @returns Promise resolving to updated result

105

*/

106

fetchPreviousPage: (options?: FetchPreviousPageOptions) => Promise<InfiniteQueryObserverResult<TData, TError>>;

107

108

/** Whether there is a next page available */

109

hasNextPage: boolean;

110

111

/** Whether there is a previous page available */

112

hasPreviousPage: boolean;

113

114

/** Whether currently fetching the next page */

115

isFetchingNextPage: boolean;

116

117

/** Whether currently fetching the previous page */

118

isFetchingPreviousPage: boolean;

119

120

/** Whether next page fetch failed */

121

isFetchNextPageError: boolean;

122

123

/** Whether previous page fetch failed */

124

isFetchPreviousPageError: boolean;

125

}

126

127

type GetNextPageParamFunction<TQueryData = unknown, TPageParam = unknown> = (

128

lastPage: TQueryData,

129

allPages: TQueryData[],

130

lastPageParam: TPageParam,

131

allPageParams: TPageParam[]

132

) => TPageParam | undefined | null;

133

134

type GetPreviousPageParamFunction<TQueryData = unknown, TPageParam = unknown> = (

135

firstPage: TQueryData,

136

allPages: TQueryData[],

137

firstPageParam: TPageParam,

138

allPageParams: TPageParam[]

139

) => TPageParam | undefined | null;

140

141

interface FetchNextPageOptions {

142

throwOnError?: boolean;

143

cancelRefetch?: boolean;

144

}

145

146

interface FetchPreviousPageOptions {

147

throwOnError?: boolean;

148

cancelRefetch?: boolean;

149

}

150

```

151

152

**Usage Examples:**

153

154

```typescript

155

import { QueryClient, InfiniteQueryObserver } from "@tanstack/query-core";

156

157

const queryClient = new QueryClient();

158

159

// Cursor-based pagination

160

const infiniteObserver = new InfiniteQueryObserver(queryClient, {

161

queryKey: ['posts'],

162

queryFn: async ({ pageParam = null }) => {

163

const url = pageParam

164

? `/api/posts?cursor=${pageParam}`

165

: '/api/posts';

166

const response = await fetch(url);

167

return response.json();

168

},

169

initialPageParam: null,

170

getNextPageParam: (lastPage) => lastPage.nextCursor ?? undefined,

171

getPreviousPageParam: (firstPage) => firstPage.prevCursor ?? undefined,

172

});

173

174

// Subscribe to changes

175

const unsubscribe = infiniteObserver.subscribe((result) => {

176

console.log('All pages:', result.data?.pages);

177

console.log('Page params:', result.data?.pageParams);

178

console.log('Has next page:', result.hasNextPage);

179

console.log('Is fetching next:', result.isFetchingNextPage);

180

181

// Flatten all posts from all pages

182

const allPosts = result.data?.pages.flatMap(page => page.posts) ?? [];

183

console.log('Total posts:', allPosts.length);

184

});

185

186

// Load next page

187

const nextPageResult = await infiniteObserver.fetchNextPage();

188

189

// Load previous page

190

const prevPageResult = await infiniteObserver.fetchPreviousPage();

191

192

// Cleanup

193

unsubscribe();

194

infiniteObserver.destroy();

195

```

196

197

### Imperative Infinite Query Methods

198

199

Direct methods on QueryClient for infinite queries.

200

201

```typescript { .api }

202

/**

203

* Fetch an infinite query imperatively

204

* @param options - Infinite query options

205

* @returns Promise resolving to infinite data

206

*/

207

fetchInfiniteQuery<T>(options: FetchInfiniteQueryOptions<T>): Promise<InfiniteData<T>>;

208

209

/**

210

* Prefetch an infinite query

211

* @param options - Infinite query options

212

* @returns Promise that resolves when prefetch completes

213

*/

214

prefetchInfiniteQuery<T>(options: FetchInfiniteQueryOptions<T>): Promise<void>;

215

216

/**

217

* Ensure infinite query data exists

218

* @param options - Infinite query options with revalidation

219

* @returns Promise resolving to infinite data

220

*/

221

ensureInfiniteQueryData<T>(options: EnsureInfiniteQueryDataOptions<T>): Promise<InfiniteData<T>>;

222

223

interface FetchInfiniteQueryOptions<T> {

224

queryKey: QueryKey;

225

queryFn: QueryFunction<T>;

226

initialPageParam: unknown;

227

getNextPageParam: GetNextPageParamFunction<T>;

228

getPreviousPageParam?: GetPreviousPageParamFunction<T>;

229

pages?: number;

230

staleTime?: number;

231

gcTime?: number;

232

retry?: RetryValue<T>;

233

networkMode?: NetworkMode;

234

meta?: QueryMeta;

235

signal?: AbortSignal;

236

}

237

238

interface EnsureInfiniteQueryDataOptions<T> extends FetchInfiniteQueryOptions<T> {

239

revalidateIfStale?: boolean;

240

}

241

```

242

243

**Usage Examples:**

244

245

```typescript

246

// Fetch infinite query imperatively

247

const infiniteData = await queryClient.fetchInfiniteQuery({

248

queryKey: ['posts'],

249

queryFn: async ({ pageParam = 0 }) => {

250

const response = await fetch(`/api/posts?page=${pageParam}`);

251

return response.json();

252

},

253

initialPageParam: 0,

254

getNextPageParam: (lastPage, pages) =>

255

lastPage.hasMore ? pages.length : undefined,

256

pages: 3, // Load first 3 pages

257

});

258

259

// Prefetch infinite query

260

await queryClient.prefetchInfiniteQuery({

261

queryKey: ['popular-posts'],

262

queryFn: async ({ pageParam = 0 }) => {

263

const response = await fetch(`/api/posts/popular?page=${pageParam}`);

264

return response.json();

265

},

266

initialPageParam: 0,

267

getNextPageParam: (lastPage, pages) =>

268

lastPage.hasMore ? pages.length : undefined,

269

pages: 2, // Prefetch first 2 pages

270

});

271

```

272

273

### InfiniteData Structure

274

275

The data structure returned by infinite queries.

276

277

```typescript { .api }

278

interface InfiniteData<TData> {

279

/**

280

* Array of all pages that have been fetched

281

* Each page contains the data returned by queryFn for that page

282

*/

283

pages: TData[];

284

285

/**

286

* Array of all page parameters that were used to fetch each page

287

* Corresponds to the pages array - pageParams[i] was used to fetch pages[i]

288

*/

289

pageParams: unknown[];

290

}

291

```

292

293

**Usage Examples:**

294

295

```typescript

296

// Working with infinite data

297

const result = infiniteObserver.getCurrentResult();

298

299

if (result.data) {

300

// Access all pages

301

console.log('Number of pages:', result.data.pages.length);

302

303

// Access specific page

304

const firstPage = result.data.pages[0];

305

const lastPage = result.data.pages[result.data.pages.length - 1];

306

307

// Access page parameters

308

console.log('Page params:', result.data.pageParams);

309

310

// Flatten all data from all pages

311

const allItems = result.data.pages.flatMap(page =>

312

Array.isArray(page) ? page : page.items || []

313

);

314

315

console.log('Total items across all pages:', allItems.length);

316

}

317

```

318

319

### Pagination Patterns

320

321

Common pagination patterns and how to implement them.

322

323

```typescript { .api }

324

// Offset-based pagination

325

const offsetPagination = new InfiniteQueryObserver(queryClient, {

326

queryKey: ['posts', 'offset'],

327

queryFn: async ({ pageParam = 0 }) => {

328

const response = await fetch(`/api/posts?offset=${pageParam}&limit=20`);

329

return response.json();

330

},

331

initialPageParam: 0,

332

getNextPageParam: (lastPage, allPages) => {

333

if (lastPage.items.length < 20) return undefined; // No more pages

334

return allPages.length * 20; // Next offset

335

},

336

});

337

338

// Cursor-based pagination

339

const cursorPagination = new InfiniteQueryObserver(queryClient, {

340

queryKey: ['posts', 'cursor'],

341

queryFn: async ({ pageParam }) => {

342

const url = pageParam

343

? `/api/posts?cursor=${pageParam}&limit=20`

344

: '/api/posts?limit=20';

345

const response = await fetch(url);

346

return response.json();

347

},

348

initialPageParam: undefined,

349

getNextPageParam: (lastPage) => lastPage.nextCursor,

350

getPreviousPageParam: (firstPage) => firstPage.prevCursor,

351

});

352

353

// Page number pagination

354

const pageNumberPagination = new InfiniteQueryObserver(queryClient, {

355

queryKey: ['posts', 'pages'],

356

queryFn: async ({ pageParam = 1 }) => {

357

const response = await fetch(`/api/posts?page=${pageParam}&limit=20`);

358

return response.json();

359

},

360

initialPageParam: 1,

361

getNextPageParam: (lastPage, allPages) => {

362

return lastPage.totalPages > allPages.length

363

? allPages.length + 1

364

: undefined;

365

},

366

getPreviousPageParam: (firstPage, allPages, firstPageParam) => {

367

return firstPageParam > 1 ? firstPageParam - 1 : undefined;

368

},

369

});

370

```

371

372

### Infinite Query Configuration

373

374

Advanced configuration options specific to infinite queries.

375

376

```typescript { .api }

377

interface InfiniteQueryObserverOptions<T> {

378

/**

379

* Maximum number of pages to keep in memory

380

* When exceeded, oldest pages are removed

381

* Useful for managing memory usage in long-running infinite queries

382

*/

383

maxPages?: number;

384

385

/**

386

* Function to determine if there are more pages to fetch forward

387

* Called with the last page and all pages

388

*/

389

getNextPageParam: GetNextPageParamFunction<T>;

390

391

/**

392

* Function to determine if there are more pages to fetch backward

393

* Called with the first page and all pages

394

*/

395

getPreviousPageParam?: GetPreviousPageParamFunction<T>;

396

397

/**

398

* Initial page parameter for the first page

399

* This is passed to queryFn as pageParam for the first fetch

400

*/

401

initialPageParam: unknown;

402

403

/**

404

* Custom select function for infinite queries

405

* Can transform the InfiniteData structure

406

*/

407

select?: (data: InfiniteData<T>) => InfiniteData<T>;

408

}

409

```

410

411

## Core Types

412

413

```typescript { .api }

414

interface QueryFunctionContext<TQueryKey extends QueryKey = QueryKey, TPageParam = unknown> {

415

queryKey: TQueryKey;

416

signal: AbortSignal;

417

meta: QueryMeta | undefined;

418

pageParam: TPageParam;

419

direction: 'forward' | 'backward';

420

}

421

422

type RetryValue<TError> = boolean | number | ((failureCount: number, error: TError) => boolean);

423

type NetworkMode = 'online' | 'always' | 'offlineFirst';

424

425

interface QueryMeta extends Record<string, unknown> {}

426

```