@apollo/client 4.1.7 → 4.1.8

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.
Files changed (27) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/__cjs/react/hooks/useSubscription.cjs +1 -1
  3. package/__cjs/react/hooks/useSubscription.cjs.map +1 -1
  4. package/__cjs/react/hooks/useSubscription.d.cts +2 -2
  5. package/__cjs/version.cjs +1 -1
  6. package/package.json +3 -2
  7. package/react/hooks/useSubscription.d.ts +2 -2
  8. package/react/hooks/useSubscription.js +1 -1
  9. package/react/hooks/useSubscription.js.map +1 -1
  10. package/react/hooks-compiled/useSubscription.d.ts +2 -2
  11. package/react/hooks-compiled/useSubscription.js +1 -1
  12. package/react/hooks-compiled/useSubscription.js.map +1 -1
  13. package/skills/apollo-client/SKILL.md +168 -0
  14. package/skills/apollo-client/references/caching.md +560 -0
  15. package/skills/apollo-client/references/error-handling.md +344 -0
  16. package/skills/apollo-client/references/fragments.md +804 -0
  17. package/skills/apollo-client/references/integration-client.md +340 -0
  18. package/skills/apollo-client/references/integration-nextjs.md +325 -0
  19. package/skills/apollo-client/references/integration-react-router.md +256 -0
  20. package/skills/apollo-client/references/integration-tanstack-start.md +378 -0
  21. package/skills/apollo-client/references/mutations.md +549 -0
  22. package/skills/apollo-client/references/queries.md +416 -0
  23. package/skills/apollo-client/references/state-management.md +428 -0
  24. package/skills/apollo-client/references/suspense-hooks.md +773 -0
  25. package/skills/apollo-client/references/troubleshooting.md +487 -0
  26. package/skills/apollo-client/references/typescript-codegen.md +133 -0
  27. package/version.js +1 -1
@@ -0,0 +1,168 @@
1
+ ---
2
+ name: apollo-client
3
+ description: >
4
+ Guide for building React applications with Apollo Client 4.x. Use this skill when:
5
+ (1) setting up Apollo Client in a React project,
6
+ (2) writing GraphQL queries or mutations with hooks,
7
+ (3) configuring caching or cache policies,
8
+ (4) managing local state with reactive variables,
9
+ (5) troubleshooting Apollo Client errors or performance issues.
10
+ license: MIT
11
+ compatibility: React 18+, React 19 (Suspense/RSC). Works with Next.js, Vite, CRA, and other React frameworks.
12
+ metadata:
13
+ author: apollographql
14
+ version: "1.0.0"
15
+ allowed-tools: Bash(npm:*) Bash(npx:*) Bash(node:*) Read Write Edit Glob Grep
16
+ ---
17
+
18
+ # Apollo Client 4.x Guide
19
+
20
+ Apollo Client is a comprehensive state management library for JavaScript that enables you to manage both local and remote data with GraphQL. Version 4.x brings improved caching, better TypeScript support, and React 19 compatibility.
21
+
22
+ ## Integration Guides
23
+
24
+ Choose the integration guide that matches your application setup:
25
+
26
+ - **[Client-Side Apps](references/integration-client.md)** - For client-side React applications without SSR (Vite, Create React App, etc.)
27
+ - **[Next.js App Router](references/integration-nextjs.md)** - For Next.js applications using the App Router with React Server Components
28
+ - **[React Router Framework Mode](references/integration-react-router.md)** - For React Router 7 applications with streaming SSR
29
+ - **[TanStack Start](references/integration-tanstack-start.md)** - For TanStack Start applications with modern routing
30
+
31
+ Each guide includes installation steps, configuration, and framework-specific patterns optimized for that environment.
32
+
33
+ ## Quick Reference
34
+
35
+ ### Basic Query
36
+
37
+ ```tsx
38
+ import { gql } from "@apollo/client";
39
+ import { useQuery } from "@apollo/client/react";
40
+
41
+ const GET_USER = gql`
42
+ query GetUser($id: ID!) {
43
+ user(id: $id) {
44
+ id
45
+ name
46
+ }
47
+ }
48
+ `;
49
+
50
+ function UserProfile({ userId }: { userId: string }) {
51
+ const { loading, error, data, dataState } = useQuery(GET_USER, {
52
+ variables: { id: userId },
53
+ });
54
+
55
+ if (loading) return <p>Loading...</p>;
56
+ if (error) return <p>Error: {error.message}</p>;
57
+
58
+ // TypeScript note: for stricter type narrowing, you can also check `dataState === "complete"` before accessing data
59
+ return <div>{data?.user.name}</div>;
60
+ }
61
+ ```
62
+
63
+ ### Basic Mutation
64
+
65
+ ```tsx
66
+ import { gql } from "@apollo/client";
67
+ import { useMutation } from "@apollo/client/react";
68
+
69
+ const CREATE_USER = gql`
70
+ mutation CreateUser($input: CreateUserInput!) {
71
+ createUser(input: $input) {
72
+ id
73
+ name
74
+ }
75
+ }
76
+ `;
77
+
78
+ function CreateUserForm() {
79
+ const [createUser, { loading, error }] = useMutation(CREATE_USER);
80
+
81
+ const handleSubmit = async (name: string) => {
82
+ await createUser({ variables: { input: { name } } });
83
+ };
84
+
85
+ return <button onClick={() => handleSubmit("John")}>Create User</button>;
86
+ }
87
+ ```
88
+
89
+ ### Suspense Query
90
+
91
+ ```tsx
92
+ import { Suspense } from "react";
93
+ import { useSuspenseQuery } from "@apollo/client/react";
94
+
95
+ function UserProfile({ userId }: { userId: string }) {
96
+ const { data } = useSuspenseQuery(GET_USER, {
97
+ variables: { id: userId },
98
+ });
99
+
100
+ return <div>{data.user.name}</div>;
101
+ }
102
+
103
+ function App() {
104
+ return (
105
+ <Suspense fallback={<p>Loading user...</p>}>
106
+ <UserProfile userId="1" />
107
+ </Suspense>
108
+ );
109
+ }
110
+ ```
111
+
112
+ ## Reference Files
113
+
114
+ Detailed documentation for specific topics:
115
+
116
+ - [TypeScript Code Generation](references/typescript-codegen.md) - GraphQL Code Generator setup for type-safe operations
117
+ - [Queries](references/queries.md) - useQuery, useLazyQuery, polling, refetching
118
+ - [Suspense Hooks](references/suspense-hooks.md) - useSuspenseQuery, useBackgroundQuery, useReadQuery, useLoadableQuery
119
+ - [Mutations](references/mutations.md) - useMutation, optimistic UI, cache updates
120
+ - [Fragments](references/fragments.md) - Fragment colocation, useFragment, useSuspenseFragment, data masking
121
+ - [Caching](references/caching.md) - InMemoryCache, typePolicies, cache manipulation
122
+ - [State Management](references/state-management.md) - Reactive variables, local state
123
+ - [Error Handling](references/error-handling.md) - Error policies, error links, retries
124
+ - [Troubleshooting](references/troubleshooting.md) - Common issues and solutions
125
+
126
+ ## Key Rules
127
+
128
+ ### Query Best Practices
129
+
130
+ - **Each page should generally only have one query, composed from colocated fragments.** Use `useFragment` or `useSuspenseFragment` in all non-page-components. Use `@defer` to allow slow fields below the fold to stream in later and avoid blocking the page load.
131
+ - **Fragments are for colocation, not reuse.** Each fragment should describe exactly the data needs of a specific component, not be shared across components for common fields. See [Fragments reference](references/fragments.md) for details on fragment colocation and data masking.
132
+ - Always handle `loading` and `error` states in UI when using non-suspenseful hooks (`useQuery`, `useLazyQuery`). When using Suspense hooks (`useSuspenseQuery`, `useBackgroundQuery`), React handles this through `<Suspense>` boundaries and error boundaries.
133
+ - Use `fetchPolicy` to control cache behavior per query
134
+ - Use the TypeScript type server to look up documentation for functions and options (Apollo Client has extensive docblocks)
135
+
136
+ ### Mutation Best Practices
137
+
138
+ - **If the schema permits, mutation return values should return everything necessary to update the cache.** Neither manual updates nor refetching should be necessary.
139
+ - If the mutation response is insufficient, carefully weigh manual cache manipulation vs refetching. Manual updates risk missing server logic. Consider optimistic updates with a granular refetch if needed.
140
+ - Handle errors gracefully in the UI
141
+ - Use `refetchQueries` sparingly (prefer letting the cache update automatically)
142
+
143
+ ### Caching Best Practices
144
+
145
+ - Configure `keyFields` for types without `id` field
146
+ - Disable normalization by setting `keyFields: false` for types that don't include an identifier and are meant to group related fields under the parent
147
+ - Use `typePolicies` for pagination and computed fields
148
+ - Understand cache normalization to debug issues
149
+ - **Enable [data masking](references/fragments.md#data-masking) for all new applications** - it prevents components from accessing fragment data they don't own, enforcing proper data boundaries and preventing over-rendering
150
+
151
+ ### Performance
152
+
153
+ - Avoid over-fetching with proper field selection
154
+ - Configure appropriate `fetchPolicy` per use case
155
+ - Use `@defer` for incremental delivery of deferred query parts, and `@stream` for streaming list fields (`@stream` available in Apollo Client 4.1+)
156
+ - **Prefer Suspense hooks (`useSuspenseQuery`, `useBackgroundQuery`) in modern applications** for better loading state handling and code simplicity
157
+
158
+ ## Ground Rules
159
+
160
+ - ALWAYS use Apollo Client 4.x patterns (not v3 or earlier)
161
+ - ALWAYS wrap your app with `ApolloProvider`
162
+ - ALWAYS handle loading and error states when using non-suspenseful hooks
163
+ - PREFER Suspense hooks (`useSuspenseQuery`, `useBackgroundQuery`) in modern applications for better DX
164
+ - NEVER store Apollo Client in React state (use module-level or context)
165
+ - PREFER `cache-first` for read-heavy data, `network-only` for real-time data
166
+ - USE TypeScript for better type safety with GraphQL
167
+ - IMPLEMENT proper cache updates instead of refetching entire queries
168
+ - ADVISE the human steering you to look into Apollo DevTools when collaboratively debugging Apollo Client issues
@@ -0,0 +1,560 @@
1
+ # Caching Reference
2
+
3
+ ## Table of Contents
4
+
5
+ - [InMemoryCache Setup](#inmemorycache-setup)
6
+ - [Cache Normalization](#cache-normalization)
7
+ - [Type Policies](#type-policies)
8
+ - [Field Policies](#field-policies)
9
+ - [Pagination](#pagination)
10
+ - [Cache Manipulation](#cache-manipulation)
11
+ - [Garbage Collection](#garbage-collection)
12
+
13
+ ## InMemoryCache Setup
14
+
15
+ ### Basic Configuration
16
+
17
+ ```typescript
18
+ import { InMemoryCache } from "@apollo/client";
19
+
20
+ const cache = new InMemoryCache({
21
+ // Custom type policies
22
+ typePolicies: {
23
+ Query: {
24
+ fields: {
25
+ // Query-level field policies
26
+ },
27
+ },
28
+ User: {
29
+ keyFields: ["id"],
30
+ fields: {
31
+ // User-level field policies
32
+ },
33
+ },
34
+ },
35
+
36
+ // Custom type name handling (rare)
37
+ possibleTypes: {
38
+ Character: ["Human", "Droid"],
39
+ Node: ["User", "Post", "Comment"],
40
+ },
41
+ });
42
+ ```
43
+
44
+ ### Constructor Options
45
+
46
+ ```typescript
47
+ new InMemoryCache({
48
+ // Define how types are identified in cache
49
+ typePolicies: {
50
+ /* ... */
51
+ },
52
+
53
+ // Interface/union type mappings between supertypes and their subtypes
54
+ possibleTypes: {
55
+ /* ... */
56
+ },
57
+
58
+ // Custom function to generate cache IDs (rare)
59
+ dataIdFromObject: (object) => {
60
+ if (object.__typename === "Book") {
61
+ return `Book:${object.isbn}`;
62
+ }
63
+ return defaultDataIdFromObject(object);
64
+ },
65
+ });
66
+ ```
67
+
68
+ ## Cache Normalization
69
+
70
+ Apollo Client normalizes data by splitting query results into individual objects and storing them by unique identifier.
71
+
72
+ ### How Normalization Works
73
+
74
+ ```graphql
75
+ # Query
76
+ query GetPost {
77
+ post(id: "1") {
78
+ id
79
+ title
80
+ author {
81
+ id
82
+ name
83
+ }
84
+ }
85
+ }
86
+ ```
87
+
88
+ ```typescript
89
+ // Normalized cache structure
90
+ {
91
+ 'Post:1': {
92
+ __typename: 'Post',
93
+ id: '1',
94
+ title: 'Hello World',
95
+ author: { __ref: 'User:42' }
96
+ },
97
+ 'User:42': {
98
+ __typename: 'User',
99
+ id: '42',
100
+ name: 'John'
101
+ },
102
+ ROOT_QUERY: {
103
+ 'post({"id":"1"})': { __ref: 'Post:1' }
104
+ }
105
+ }
106
+ ```
107
+
108
+ ### Benefits of Normalization
109
+
110
+ 1. **Automatic updates**: When `User:42` is updated anywhere, all components showing that user update
111
+ 2. **Deduplication**: Same objects aren't stored multiple times
112
+ 3. **Efficient updates**: Only changed objects trigger re-renders
113
+
114
+ ## Type Policies
115
+
116
+ ### keyFields
117
+
118
+ Customize how objects are identified in the cache.
119
+
120
+ ```typescript
121
+ const cache = new InMemoryCache({
122
+ typePolicies: {
123
+ // Use ISBN instead of id for books
124
+ Book: {
125
+ keyFields: ["isbn"],
126
+ },
127
+
128
+ // Composite key
129
+ UserSession: {
130
+ keyFields: ["userId", "deviceId"],
131
+ },
132
+
133
+ // Nested key
134
+ Review: {
135
+ keyFields: ["book", ["isbn"], "reviewer", ["id"]],
136
+ },
137
+
138
+ // No key fields (singleton, only one object in cache per type)
139
+ AppSettings: {
140
+ keyFields: [],
141
+ },
142
+
143
+ // Disable normalization (objects of this type will be stored with their
144
+ // parent entity. The same object might end up multiple times in the cache
145
+ // and run out of sync. Use with caution, only if this object really relates
146
+ // to a property of their parent entity and cannot exist on its own.)
147
+ Address: {
148
+ keyFields: false,
149
+ },
150
+ },
151
+ });
152
+ ```
153
+
154
+ ### merge Functions
155
+
156
+ Control how incoming data merges with existing data.
157
+
158
+ ```typescript
159
+ const cache = new InMemoryCache({
160
+ typePolicies: {
161
+ User: {
162
+ fields: {
163
+ // Deep merge profile object
164
+ profile: {
165
+ merge: true, // Shorthand for deep merge
166
+ },
167
+
168
+ // Custom merge logic
169
+ notifications: {
170
+ merge(existing = [], incoming, { mergeObjects }) {
171
+ // Prepend new notifications
172
+ return [...incoming, ...existing];
173
+ },
174
+ },
175
+ },
176
+ },
177
+ },
178
+ });
179
+ ```
180
+
181
+ ## Field Policies
182
+
183
+ ### read Function
184
+
185
+ Transform cached data when reading.
186
+
187
+ ```typescript
188
+ const cache = new InMemoryCache({
189
+ typePolicies: {
190
+ User: {
191
+ fields: {
192
+ // Computed field
193
+ fullName: {
194
+ read(_, { readField }) {
195
+ const firstName = readField("firstName");
196
+ const lastName = readField("lastName");
197
+ return `${firstName} ${lastName}`;
198
+ },
199
+ },
200
+
201
+ // Transform existing field
202
+ birthDate: {
203
+ read(existing) {
204
+ return existing ? new Date(existing) : null;
205
+ },
206
+ },
207
+
208
+ // Default value
209
+ role: {
210
+ read(existing = "USER") {
211
+ return existing;
212
+ },
213
+ },
214
+ },
215
+ },
216
+ },
217
+ });
218
+ ```
219
+
220
+ ### merge Function
221
+
222
+ Control how incoming data is stored.
223
+
224
+ ```typescript
225
+ const cache = new InMemoryCache({
226
+ typePolicies: {
227
+ User: {
228
+ fields: {
229
+ // Accumulate items instead of replacing
230
+ friends: {
231
+ merge(existing = [], incoming) {
232
+ return [...existing, ...incoming];
233
+ },
234
+ },
235
+
236
+ // Merge objects deeply
237
+ settings: {
238
+ merge(existing, incoming, { mergeObjects }) {
239
+ return mergeObjects(existing, incoming);
240
+ },
241
+ },
242
+ },
243
+ },
244
+
245
+ Query: {
246
+ fields: {
247
+ // Merge paginated results
248
+ posts: {
249
+ keyArgs: ["category"], // Only category affects cache key
250
+ merge(existing = { items: [] }, incoming) {
251
+ return {
252
+ ...incoming,
253
+ items: [...existing.items, ...incoming.items],
254
+ };
255
+ },
256
+ },
257
+ },
258
+ },
259
+ },
260
+ });
261
+ ```
262
+
263
+ ### keyArgs
264
+
265
+ Control which arguments affect cache storage.
266
+
267
+ ```typescript
268
+ const cache = new InMemoryCache({
269
+ typePolicies: {
270
+ Query: {
271
+ fields: {
272
+ // Different cache entry per userId only
273
+ // (limit, offset don't create new entries)
274
+ userPosts: {
275
+ keyArgs: ["userId"],
276
+ },
277
+
278
+ // No arguments affect cache key
279
+ // (useful for pagination)
280
+ feed: {
281
+ keyArgs: false,
282
+ },
283
+
284
+ // Nested argument
285
+ search: {
286
+ keyArgs: ["filter", ["category", "status"]],
287
+ },
288
+ },
289
+ },
290
+ },
291
+ });
292
+ ```
293
+
294
+ ## Pagination
295
+
296
+ ### Offset-Based Pagination
297
+
298
+ ```typescript
299
+ import { offsetLimitPagination } from "@apollo/client/utilities";
300
+
301
+ const cache = new InMemoryCache({
302
+ typePolicies: {
303
+ Query: {
304
+ fields: {
305
+ posts: offsetLimitPagination(),
306
+
307
+ // With key arguments
308
+ userPosts: offsetLimitPagination(["userId"]),
309
+ },
310
+ },
311
+ },
312
+ });
313
+ ```
314
+
315
+ ### Cursor-Based Pagination (Relay Style)
316
+
317
+ ```typescript
318
+ import { relayStylePagination } from "@apollo/client/utilities";
319
+
320
+ const cache = new InMemoryCache({
321
+ typePolicies: {
322
+ Query: {
323
+ fields: {
324
+ posts: relayStylePagination(),
325
+
326
+ // With key arguments
327
+ userPosts: relayStylePagination(["userId"]),
328
+ },
329
+ },
330
+ },
331
+ });
332
+ ```
333
+
334
+ ### Custom Pagination
335
+
336
+ ```typescript
337
+ const cache = new InMemoryCache({
338
+ typePolicies: {
339
+ Query: {
340
+ fields: {
341
+ feed: {
342
+ keyArgs: false,
343
+
344
+ merge(existing, incoming, { args }) {
345
+ const merged = existing ? existing.slice(0) : [];
346
+ const offset = args?.offset ?? 0;
347
+
348
+ for (let i = 0; i < incoming.length; i++) {
349
+ merged[offset + i] = incoming[i];
350
+ }
351
+
352
+ return merged;
353
+ },
354
+
355
+ read(existing, { args }) {
356
+ const offset = args?.offset ?? 0;
357
+ const limit = args?.limit ?? existing?.length ?? 0;
358
+ return existing?.slice(offset, offset + limit);
359
+ },
360
+ },
361
+ },
362
+ },
363
+ },
364
+ });
365
+ ```
366
+
367
+ ### fetchMore for Pagination
368
+
369
+ ```tsx
370
+ function PostList() {
371
+ const { data, fetchMore, loading } = useQuery(GET_POSTS, {
372
+ variables: { offset: 0, limit: 10 },
373
+ });
374
+
375
+ const loadMore = () => {
376
+ fetchMore({
377
+ variables: {
378
+ offset: data.posts.length,
379
+ },
380
+ // With proper type policies, no updateQuery needed
381
+ });
382
+ };
383
+
384
+ return (
385
+ <div>
386
+ {data?.posts.map((post) => <PostCard key={post.id} post={post} />)}
387
+ <button onClick={loadMore} disabled={loading}>
388
+ Load More
389
+ </button>
390
+ </div>
391
+ );
392
+ }
393
+ ```
394
+
395
+ ## Cache Manipulation
396
+
397
+ ### cache.readQuery
398
+
399
+ ```typescript
400
+ // Read data from cache
401
+ const data = cache.readQuery({
402
+ query: GET_TODOS,
403
+ });
404
+
405
+ // With variables
406
+ const userData = cache.readQuery({
407
+ query: GET_USER,
408
+ variables: { id: "1" },
409
+ });
410
+ ```
411
+
412
+ ### cache.writeQuery
413
+
414
+ ```typescript
415
+ // Write data to cache
416
+ cache.writeQuery({
417
+ query: GET_TODOS,
418
+ data: {
419
+ todos: [
420
+ { __typename: "Todo", id: "1", text: "Buy milk", completed: false },
421
+ ],
422
+ },
423
+ });
424
+
425
+ // With variables
426
+ cache.writeQuery({
427
+ query: GET_USER,
428
+ variables: { id: "1" },
429
+ data: {
430
+ user: { __typename: "User", id: "1", name: "John" },
431
+ },
432
+ });
433
+ ```
434
+
435
+ ### cache.readFragment / cache.writeFragment
436
+
437
+ ```typescript
438
+ // Read a specific object - use cache.identify for safety
439
+ const user = cache.readFragment({
440
+ id: cache.identify({ __typename: "User", id: "1" }),
441
+ fragment: gql`
442
+ fragment UserFragment on User {
443
+ id
444
+ name
445
+ email
446
+ }
447
+ `,
448
+ });
449
+
450
+ // Apollo Client 4.1+: Use 'from' parameter (recommended)
451
+ const user = cache.readFragment({
452
+ from: { __typename: "User", id: "1" },
453
+ fragment: gql`
454
+ fragment UserFragment on User {
455
+ id
456
+ name
457
+ email
458
+ }
459
+ `,
460
+ });
461
+
462
+ // Update a specific object
463
+ cache.writeFragment({
464
+ id: cache.identify({ __typename: "User", id: "1" }),
465
+ fragment: gql`
466
+ fragment UpdateUser on User {
467
+ name
468
+ }
469
+ `,
470
+ data: {
471
+ name: "Jane",
472
+ },
473
+ });
474
+
475
+ // Apollo Client 4.1+: Use 'from' parameter (recommended)
476
+ cache.writeFragment({
477
+ from: { __typename: "User", id: "1" },
478
+ fragment: gql`
479
+ fragment UpdateUser on User {
480
+ name
481
+ }
482
+ `,
483
+ data: {
484
+ name: "Jane",
485
+ },
486
+ });
487
+ ```
488
+
489
+ ### cache.modify
490
+
491
+ ```typescript
492
+ // Modify fields directly
493
+ cache.modify({
494
+ id: cache.identify(user),
495
+ fields: {
496
+ // Set new value
497
+ name: () => "New Name",
498
+
499
+ // Transform existing value
500
+ postCount: (existing) => existing + 1,
501
+
502
+ // Delete field
503
+ temporaryField: (_, { DELETE }) => DELETE,
504
+
505
+ // Add to array
506
+ friends: (existing, { toReference }) => [
507
+ ...existing,
508
+ toReference({ __typename: "User", id: "2" }),
509
+ ],
510
+ },
511
+ });
512
+ ```
513
+
514
+ ### cache.evict
515
+
516
+ ```typescript
517
+ // Remove object from cache
518
+ cache.evict({ id: "User:1" });
519
+
520
+ // Remove specific field
521
+ cache.evict({ id: "User:1", fieldName: "friends" });
522
+
523
+ // Remove with broadcast (trigger re-renders)
524
+ cache.evict({ id: "User:1", broadcast: true });
525
+ ```
526
+
527
+ ## Garbage Collection
528
+
529
+ ### Manual Garbage Collection
530
+
531
+ ```typescript
532
+ // After evicting objects, clean up dangling references
533
+ cache.evict({ id: "User:1" });
534
+ cache.gc();
535
+ ```
536
+
537
+ ### Retaining Objects
538
+
539
+ ```typescript
540
+ // Prevent objects from being garbage collected
541
+ const release = cache.retain("User:1");
542
+
543
+ // Later, allow GC
544
+ release();
545
+ cache.gc();
546
+ ```
547
+
548
+ ### Inspecting Cache
549
+
550
+ ```typescript
551
+ // Get all cached data
552
+ const cacheContents = cache.extract();
553
+
554
+ // Restore cache state
555
+ cache.restore(previousCacheContents);
556
+
557
+ // Get identified object cache key
558
+ const userId = cache.identify({ __typename: "User", id: "1" });
559
+ // Returns: 'User:1'
560
+ ```