0
# Query Utilities
1
2
Comprehensive utility functions for imperative query operations and cache management. These utilities are accessed through the `useUtils()` hook and provide programmatic control over React Query's cache and data fetching.
3
4
## Capabilities
5
6
### useUtils Hook
7
8
Primary hook for accessing query utilities and cache management functions.
9
10
```typescript { .api }
11
/**
12
* Hook that provides access to query utilities for imperative operations
13
* @returns Object with utility functions mirroring your router structure
14
*/
15
function useUtils(): CreateReactUtils<TRouter, TSSRContext>;
16
17
// Legacy alias (deprecated)
18
function useContext(): CreateReactUtils<TRouter, TSSRContext>;
19
20
interface CreateReactUtils<TRouter, TSSRContext> {
21
// Utility functions are generated based on your router structure
22
// Each procedure gets a set of utility methods
23
}
24
```
25
26
**Usage Examples:**
27
28
```typescript
29
import { trpc } from "./utils/trpc";
30
31
function UserManagement() {
32
const utils = trpc.useUtils();
33
34
const handleRefreshUser = (userId: number) => {
35
// Refetch specific user data
36
utils.user.get.refetch({ id: userId });
37
};
38
39
const handleInvalidateUsers = () => {
40
// Invalidate all user-related queries
41
utils.user.invalidate();
42
};
43
44
return (
45
<div>
46
<button onClick={() => handleRefreshUser(1)}>
47
Refresh User 1
48
</button>
49
<button onClick={handleInvalidateUsers}>
50
Refresh All User Data
51
</button>
52
</div>
53
);
54
}
55
```
56
57
### Query Invalidation
58
59
Invalidate cached queries to trigger refetching when data may be stale.
60
61
```typescript { .api }
62
/**
63
* Invalidate queries to trigger refetching
64
* @param input - Optional input to target specific queries
65
* @param opts - Invalidation options
66
* @returns Promise that resolves when invalidation is complete
67
*/
68
procedure.invalidate(
69
input?: GetQueryProcedureInput<TInput>,
70
opts?: InvalidateQueryFilters & RefetchOptions
71
): Promise<void>;
72
73
interface InvalidateQueryFilters {
74
type?: QueryType;
75
exact?: boolean;
76
predicate?: (query: Query) => boolean;
77
}
78
```
79
80
**Usage Examples:**
81
82
```typescript
83
function InvalidationExamples() {
84
const utils = trpc.useUtils();
85
86
const invalidateExamples = {
87
// Invalidate all user queries
88
invalidateAllUsers: () => utils.user.invalidate(),
89
90
// Invalidate specific user query
91
invalidateSpecificUser: (userId: number) =>
92
utils.user.get.invalidate({ id: userId }),
93
94
// Invalidate with options
95
invalidateWithOptions: () =>
96
utils.user.list.invalidate(undefined, {
97
type: 'active', // Only invalidate active queries
98
refetchType: 'active',
99
}),
100
101
// Invalidate multiple related queries
102
invalidateUserData: async (userId: number) => {
103
await Promise.all([
104
utils.user.get.invalidate({ id: userId }),
105
utils.user.posts.invalidate({ userId }),
106
utils.user.settings.invalidate({ userId }),
107
]);
108
},
109
};
110
111
return (
112
<div>
113
<button onClick={invalidateExamples.invalidateAllUsers}>
114
Invalidate All Users
115
</button>
116
<button onClick={() => invalidateExamples.invalidateSpecificUser(1)}>
117
Invalidate User 1
118
</button>
119
</div>
120
);
121
}
122
```
123
124
### Query Refetching
125
126
Explicitly refetch queries to get fresh data from the server.
127
128
```typescript { .api }
129
/**
130
* Refetch queries to get fresh data
131
* @param input - Optional input to target specific queries
132
* @param opts - Refetch options
133
* @returns Promise that resolves when refetch is complete
134
*/
135
procedure.refetch(
136
input?: GetQueryProcedureInput<TInput>,
137
opts?: RefetchOptions
138
): Promise<void>;
139
140
interface RefetchOptions {
141
type?: 'active' | 'inactive' | 'all';
142
cancelRefetch?: boolean;
143
}
144
```
145
146
**Usage Examples:**
147
148
```typescript
149
function RefetchExamples() {
150
const utils = trpc.useUtils();
151
152
const refetchExamples = {
153
// Refetch all user queries
154
refetchAllUsers: () => utils.user.refetch(),
155
156
// Refetch specific user
157
refetchSpecificUser: (userId: number) =>
158
utils.user.get.refetch({ id: userId }),
159
160
// Refetch with options
161
refetchActive: () =>
162
utils.user.list.refetch(undefined, { type: 'active' }),
163
164
// Conditional refetch
165
conditionalRefetch: async (userId: number) => {
166
const userData = utils.user.get.getData({ id: userId });
167
if (!userData || isStale(userData)) {
168
await utils.user.get.refetch({ id: userId });
169
}
170
},
171
};
172
173
return (
174
<div>
175
<button onClick={refetchExamples.refetchAllUsers}>
176
Refetch All Users
177
</button>
178
<button onClick={() => refetchExamples.refetchSpecificUser(1)}>
179
Refetch User 1
180
</button>
181
</div>
182
);
183
}
184
```
185
186
### Query Cancellation
187
188
Cancel in-flight queries to prevent unnecessary network requests.
189
190
```typescript { .api }
191
/**
192
* Cancel in-flight queries
193
* @param input - Optional input to target specific queries
194
* @returns Promise that resolves when cancellation is complete
195
*/
196
procedure.cancel(
197
input?: GetQueryProcedureInput<TInput>
198
): Promise<void>;
199
```
200
201
**Usage Examples:**
202
203
```typescript
204
function CancellationExamples() {
205
const utils = trpc.useUtils();
206
207
const cancelExamples = {
208
// Cancel all user queries
209
cancelAllUserQueries: () => utils.user.cancel(),
210
211
// Cancel specific user query
212
cancelSpecificUser: (userId: number) =>
213
utils.user.get.cancel({ id: userId }),
214
215
// Cancel before new operation
216
cancelAndRefetch: async (userId: number) => {
217
await utils.user.get.cancel({ id: userId });
218
await utils.user.get.refetch({ id: userId });
219
},
220
};
221
222
return (
223
<div>
224
<button onClick={cancelExamples.cancelAllUserQueries}>
225
Cancel All User Queries
226
</button>
227
</div>
228
);
229
}
230
```
231
232
### Data Manipulation
233
234
Directly manipulate cached query data without making server requests.
235
236
```typescript { .api }
237
/**
238
* Set query data in the cache
239
* @param input - Input parameters identifying the query
240
* @param updater - Function or value to update the data
241
* @param opts - Set data options
242
*/
243
procedure.setData(
244
input: TInput,
245
updater: Updater<TOutput>,
246
opts?: SetDataOptions
247
): TOutput | undefined;
248
249
/**
250
* Get query data from the cache
251
* @param input - Input parameters identifying the query
252
* @returns Cached data or undefined
253
*/
254
procedure.getData(
255
input?: GetQueryProcedureInput<TInput>
256
): TOutput | undefined;
257
258
type Updater<TData> = TData | ((oldData: TData | undefined) => TData | undefined);
259
260
interface SetDataOptions {
261
updatedAt?: number;
262
}
263
```
264
265
**Usage Examples:**
266
267
```typescript
268
function DataManipulationExamples() {
269
const utils = trpc.useUtils();
270
271
const dataExamples = {
272
// Set user data directly
273
setUserData: (userId: number, userData: User) => {
274
utils.user.get.setData({ id: userId }, userData);
275
},
276
277
// Update user data with function
278
updateUserData: (userId: number, updates: Partial<User>) => {
279
utils.user.get.setData({ id: userId }, (oldData) =>
280
oldData ? { ...oldData, ...updates } : undefined
281
);
282
},
283
284
// Get user data from cache
285
getUserData: (userId: number) => {
286
return utils.user.get.getData({ id: userId });
287
},
288
289
// Optimistic update pattern
290
optimisticUpdate: (userId: number, updates: Partial<User>) => {
291
const previousData = utils.user.get.getData({ id: userId });
292
293
// Apply optimistic update
294
utils.user.get.setData({ id: userId }, (old) =>
295
old ? { ...old, ...updates } : undefined
296
);
297
298
return () => {
299
// Rollback function
300
utils.user.get.setData({ id: userId }, previousData);
301
};
302
},
303
};
304
305
const handleOptimisticUpdate = () => {
306
const rollback = dataExamples.optimisticUpdate(1, { name: "New Name" });
307
308
// Later, if the mutation fails:
309
// rollback();
310
};
311
312
return (
313
<button onClick={handleOptimisticUpdate}>
314
Optimistic Update
315
</button>
316
);
317
}
318
```
319
320
### Infinite Query Utilities
321
322
Specialized utilities for managing infinite/paginated query data.
323
324
```typescript { .api }
325
/**
326
* Set infinite query data in the cache
327
* @param input - Input parameters identifying the infinite query
328
* @param updater - Function or value to update the infinite data
329
* @param opts - Set data options
330
*/
331
procedure.setInfiniteData(
332
input: GetInfiniteQueryInput<TInput>,
333
updater: Updater<InfiniteData<TOutput>>,
334
opts?: SetDataOptions
335
): InfiniteData<TOutput> | undefined;
336
337
/**
338
* Get infinite query data from the cache
339
* @param input - Input parameters identifying the infinite query
340
* @returns Cached infinite data or undefined
341
*/
342
procedure.getInfiniteData(
343
input?: GetInfiniteQueryInput<TInput>
344
): InfiniteData<TOutput> | undefined;
345
```
346
347
**Usage Examples:**
348
349
```typescript
350
function InfiniteQueryExamples() {
351
const utils = trpc.useUtils();
352
353
const infiniteExamples = {
354
// Add new item to infinite list
355
addItemToInfiniteList: (newPost: Post) => {
356
utils.posts.list.setInfiniteData({ limit: 10 }, (oldData) => {
357
if (!oldData) return oldData;
358
359
return {
360
...oldData,
361
pages: [
362
{ ...oldData.pages[0], posts: [newPost, ...oldData.pages[0].posts] },
363
...oldData.pages.slice(1),
364
],
365
};
366
});
367
},
368
369
// Update item in infinite list
370
updateItemInInfiniteList: (postId: number, updates: Partial<Post>) => {
371
utils.posts.list.setInfiniteData({ limit: 10 }, (oldData) => {
372
if (!oldData) return oldData;
373
374
return {
375
...oldData,
376
pages: oldData.pages.map((page) => ({
377
...page,
378
posts: page.posts.map((post) =>
379
post.id === postId ? { ...post, ...updates } : post
380
),
381
})),
382
};
383
});
384
},
385
386
// Get specific item from infinite data
387
getItemFromInfiniteList: (postId: number) => {
388
const data = utils.posts.list.getInfiniteData({ limit: 10 });
389
return data?.pages
390
.flatMap((page) => page.posts)
391
.find((post) => post.id === postId);
392
},
393
};
394
395
return (
396
<button onClick={() => infiniteExamples.addItemToInfiniteList({
397
id: Date.now(),
398
title: "New Post",
399
content: "Post content",
400
})}>
401
Add Post to Infinite List
402
</button>
403
);
404
}
405
```
406
407
### Imperative Fetching
408
409
Fetch data imperatively outside of React components.
410
411
```typescript { .api }
412
/**
413
* Fetch query data imperatively
414
* @param input - Input parameters for the query
415
* @param opts - Fetch options
416
* @returns Promise resolving to the fetched data
417
*/
418
procedure.fetch(
419
input: TInput,
420
opts?: TRPCFetchQueryOptions<TOutput, TError>
421
): Promise<TOutput>;
422
423
/**
424
* Fetch infinite query data imperatively
425
* @param input - Input parameters for the infinite query
426
* @param opts - Fetch infinite options
427
* @returns Promise resolving to the fetched infinite data
428
*/
429
procedure.fetchInfinite(
430
input: GetInfiniteQueryInput<TInput>,
431
opts: TRPCFetchInfiniteQueryOptions<TInput, TOutput, TError>
432
): Promise<InfiniteData<TOutput>>;
433
```
434
435
**Usage Examples:**
436
437
```typescript
438
function ImperativeFetchingExamples() {
439
const utils = trpc.useUtils();
440
441
const fetchExamples = {
442
// Fetch user data imperatively
443
fetchUserData: async (userId: number) => {
444
try {
445
const userData = await utils.user.get.fetch({ id: userId });
446
console.log("Fetched user:", userData);
447
return userData;
448
} catch (error) {
449
console.error("Failed to fetch user:", error);
450
throw error;
451
}
452
},
453
454
// Fetch with options
455
fetchWithOptions: async (userId: number) => {
456
const userData = await utils.user.get.fetch(
457
{ id: userId },
458
{
459
staleTime: 10 * 60 * 1000, // 10 minutes
460
}
461
);
462
return userData;
463
},
464
465
// Fetch infinite data
466
fetchInfiniteData: async () => {
467
const postsData = await utils.posts.list.fetchInfinite(
468
{ limit: 10 },
469
{
470
initialCursor: null,
471
}
472
);
473
return postsData;
474
},
475
};
476
477
const handleImperativeFetch = async () => {
478
const user = await fetchExamples.fetchUserData(1);
479
console.log("User fetched:", user);
480
};
481
482
return (
483
<button onClick={handleImperativeFetch}>
484
Fetch User Imperatively
485
</button>
486
);
487
}
488
```
489
490
### Data Prefetching
491
492
Prefetch data before it's needed to improve user experience.
493
494
```typescript { .api }
495
/**
496
* Prefetch query data
497
* @param input - Input parameters for the query
498
* @param opts - Prefetch options
499
* @returns Promise that resolves when prefetch is complete
500
*/
501
procedure.prefetch(
502
input: TInput,
503
opts?: TRPCFetchQueryOptions<TOutput, TError>
504
): Promise<void>;
505
506
/**
507
* Prefetch infinite query data
508
* @param input - Input parameters for the infinite query
509
* @param opts - Prefetch infinite options
510
* @returns Promise that resolves when prefetch is complete
511
*/
512
procedure.prefetchInfinite(
513
input: GetInfiniteQueryInput<TInput>,
514
opts: TRPCFetchInfiniteQueryOptions<TInput, TOutput, TError>
515
): Promise<void>;
516
```
517
518
**Usage Examples:**
519
520
```typescript
521
function PrefetchingExamples() {
522
const utils = trpc.useUtils();
523
524
const prefetchExamples = {
525
// Prefetch on hover
526
prefetchOnHover: (userId: number) => {
527
utils.user.get.prefetch({ id: userId });
528
},
529
530
// Prefetch related data
531
prefetchRelatedData: async (userId: number) => {
532
await Promise.all([
533
utils.user.get.prefetch({ id: userId }),
534
utils.user.posts.prefetch({ userId }),
535
utils.user.settings.prefetch({ userId }),
536
]);
537
},
538
539
// Prefetch infinite data
540
prefetchInfiniteData: () => {
541
utils.posts.list.prefetchInfinite(
542
{ limit: 10 },
543
{ initialCursor: null }
544
);
545
},
546
};
547
548
return (
549
<div>
550
<button
551
onMouseEnter={() => prefetchExamples.prefetchOnHover(1)}
552
>
553
Hover to Prefetch User 1
554
</button>
555
</div>
556
);
557
}
558
```
559
560
### Data Ensuring
561
562
Ensure data exists in cache, fetching it if necessary.
563
564
```typescript { .api }
565
/**
566
* Ensure query data exists in cache, fetching if necessary
567
* @param input - Input parameters for the query
568
* @param opts - Ensure data options
569
* @returns Promise resolving to the ensured data
570
*/
571
procedure.ensureData(
572
input: TInput,
573
opts?: TRPCFetchQueryOptions<TOutput, TError>
574
): Promise<TOutput>;
575
```
576
577
**Usage Examples:**
578
579
```typescript
580
function DataEnsuringExamples() {
581
const utils = trpc.useUtils();
582
583
const ensureExamples = {
584
// Ensure user data exists before using it
585
ensureUserData: async (userId: number) => {
586
const userData = await utils.user.get.ensureData({ id: userId });
587
// userData is guaranteed to exist
588
return userData;
589
},
590
591
// Ensure data with fresh fetch if stale
592
ensureFreshData: async (userId: number) => {
593
const userData = await utils.user.get.ensureData(
594
{ id: userId },
595
{
596
staleTime: 0, // Always fetch fresh data
597
}
598
);
599
return userData;
600
},
601
};
602
603
const handleEnsureData = async () => {
604
const user = await ensureExamples.ensureUserData(1);
605
console.log("Ensured user data:", user);
606
};
607
608
return (
609
<button onClick={handleEnsureData}>
610
Ensure User Data
611
</button>
612
);
613
}
614
```
615
616
## Common Patterns
617
618
### Cache Management
619
620
```typescript
621
function CacheManagementExample() {
622
const utils = trpc.useUtils();
623
624
const cacheManager = {
625
// Clear all cache
626
clearAll: () => {
627
utils.invalidate();
628
},
629
630
// Clear specific cache
631
clearUserCache: () => {
632
utils.user.invalidate();
633
},
634
635
// Warm cache on app start
636
warmCache: async () => {
637
await Promise.all([
638
utils.user.list.prefetch(),
639
utils.posts.list.prefetch({ limit: 10 }),
640
]);
641
},
642
};
643
644
return (
645
<div>
646
<button onClick={cacheManager.clearAll}>Clear All Cache</button>
647
<button onClick={cacheManager.warmCache}>Warm Cache</button>
648
</div>
649
);
650
}
651
```
652
653
### Optimistic Updates with Rollback
654
655
```typescript
656
function OptimisticUpdatesExample() {
657
const utils = trpc.useUtils();
658
const updateUser = trpc.user.update.useMutation();
659
660
const performOptimisticUpdate = async (userId: number, updates: Partial<User>) => {
661
// Store current data for rollback
662
const previousData = utils.user.get.getData({ id: userId });
663
664
// Apply optimistic update
665
utils.user.get.setData({ id: userId }, (old) =>
666
old ? { ...old, ...updates } : undefined
667
);
668
669
try {
670
// Perform actual mutation
671
await updateUser.mutateAsync({ id: userId, ...updates });
672
} catch (error) {
673
// Rollback on error
674
utils.user.get.setData({ id: userId }, previousData);
675
throw error;
676
}
677
};
678
679
return (
680
<button onClick={() => performOptimisticUpdate(1, { name: "New Name" })}>
681
Update with Optimistic UI
682
</button>
683
);
684
}
685
```