@apollo/client 4.1.6 → 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 (33) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/__cjs/link/ws/index.cjs +9 -1
  3. package/__cjs/link/ws/index.cjs.map +1 -1
  4. package/__cjs/link/ws/index.d.cts +1 -1
  5. package/__cjs/react/hooks/useSubscription.cjs +1 -1
  6. package/__cjs/react/hooks/useSubscription.cjs.map +1 -1
  7. package/__cjs/react/hooks/useSubscription.d.cts +2 -2
  8. package/__cjs/version.cjs +1 -1
  9. package/link/ws/index.d.ts +1 -1
  10. package/link/ws/index.js +9 -1
  11. package/link/ws/index.js.map +1 -1
  12. package/package.json +3 -2
  13. package/react/hooks/useSubscription.d.ts +2 -2
  14. package/react/hooks/useSubscription.js +1 -1
  15. package/react/hooks/useSubscription.js.map +1 -1
  16. package/react/hooks-compiled/useSubscription.d.ts +2 -2
  17. package/react/hooks-compiled/useSubscription.js +1 -1
  18. package/react/hooks-compiled/useSubscription.js.map +1 -1
  19. package/skills/apollo-client/SKILL.md +168 -0
  20. package/skills/apollo-client/references/caching.md +560 -0
  21. package/skills/apollo-client/references/error-handling.md +344 -0
  22. package/skills/apollo-client/references/fragments.md +804 -0
  23. package/skills/apollo-client/references/integration-client.md +340 -0
  24. package/skills/apollo-client/references/integration-nextjs.md +325 -0
  25. package/skills/apollo-client/references/integration-react-router.md +256 -0
  26. package/skills/apollo-client/references/integration-tanstack-start.md +378 -0
  27. package/skills/apollo-client/references/mutations.md +549 -0
  28. package/skills/apollo-client/references/queries.md +416 -0
  29. package/skills/apollo-client/references/state-management.md +428 -0
  30. package/skills/apollo-client/references/suspense-hooks.md +773 -0
  31. package/skills/apollo-client/references/troubleshooting.md +487 -0
  32. package/skills/apollo-client/references/typescript-codegen.md +133 -0
  33. package/version.js +1 -1
@@ -0,0 +1,487 @@
1
+ # Troubleshooting Reference
2
+
3
+ ## Table of Contents
4
+
5
+ - [Setup Issues](#setup-issues)
6
+ - [Cache Issues](#cache-issues)
7
+ - [TypeScript Issues](#typescript-issues)
8
+ - [Performance Issues](#performance-issues)
9
+ - [DevTools Usage](#devtools-usage)
10
+ - [Common Error Messages](#common-error-messages)
11
+
12
+ ## Setup Issues
13
+
14
+ ### Provider Not Found
15
+
16
+ **Error:** `Could not find "client" in the context or passed in as an option`
17
+
18
+ **Cause:** Component is not wrapped with `ApolloProvider`.
19
+
20
+ **Solution:**
21
+
22
+ ```tsx
23
+ // Ensure ApolloProvider wraps your app
24
+ import { ApolloProvider } from "@apollo/client";
25
+
26
+ function App() {
27
+ return (
28
+ <ApolloProvider client={client}>
29
+ <YourApp />
30
+ </ApolloProvider>
31
+ );
32
+ }
33
+ ```
34
+
35
+ ### Multiple Apollo Clients
36
+
37
+ **Problem:** Unintended cache isolation or conflicting states.
38
+
39
+ **Solution:** Use a single client instance or explicitly manage multiple clients:
40
+
41
+ ```tsx
42
+ // Single client (recommended)
43
+ const client = new ApolloClient({
44
+ /* ... */
45
+ });
46
+
47
+ export function App() {
48
+ return (
49
+ <ApolloProvider client={client}>
50
+ <Router />
51
+ </ApolloProvider>
52
+ );
53
+ }
54
+
55
+ // Multiple clients (rare use case)
56
+ const publicClient = new ApolloClient({
57
+ uri: "/public/graphql",
58
+ cache: new InMemoryCache(),
59
+ });
60
+ const adminClient = new ApolloClient({
61
+ uri: "/admin/graphql",
62
+ cache: new InMemoryCache(),
63
+ });
64
+
65
+ function AdminSection() {
66
+ return (
67
+ <ApolloProvider client={adminClient}>
68
+ <AdminDashboard />
69
+ </ApolloProvider>
70
+ );
71
+ }
72
+ ```
73
+
74
+ ### Client Created in Component
75
+
76
+ **Problem:** New client on every render causes cache loss.
77
+
78
+ **Solution:** Create client outside component or use a ref pattern:
79
+
80
+ ```tsx
81
+ // Bad - new client on every render
82
+ function App() {
83
+ const client = new ApolloClient({
84
+ /* ... */
85
+ }); // Don't do this!
86
+ return <ApolloProvider client={client}>...</ApolloProvider>;
87
+ }
88
+
89
+ // Module-level client definition
90
+ // Okay if there is a 100% guarantee this application will never use SSR
91
+ const client = new ApolloClient({
92
+ /* ... */
93
+ });
94
+ function App() {
95
+ return <ApolloProvider client={client}>...</ApolloProvider>;
96
+ }
97
+
98
+ // Good - store Apollo Client in a ref that is initialized once
99
+ function useApolloClient(makeApolloClient: () => ApolloClient): ApolloClient {
100
+ const storeRef = useRef<ApolloClient | null>(null);
101
+ if (!storeRef.current) {
102
+ storeRef.current = makeApolloClient();
103
+ }
104
+ return storeRef.current;
105
+ }
106
+
107
+ // Better - singleton global in non-SSR environments to survive unmounts
108
+ const singleton = Symbol.for("ApolloClientSingleton");
109
+ declare global {
110
+ interface Window {
111
+ [singleton]?: ApolloClient;
112
+ }
113
+ }
114
+
115
+ function useApolloClient(makeApolloClient: () => ApolloClient): ApolloClient {
116
+ const storeRef = useRef<ApolloClient | null>(null);
117
+ if (!storeRef.current) {
118
+ if (typeof window === "undefined") {
119
+ storeRef.current = makeApolloClient();
120
+ } else {
121
+ window[singleton] ??= makeApolloClient();
122
+ storeRef.current = window[singleton];
123
+ }
124
+ }
125
+ return storeRef.current;
126
+ }
127
+ // Note: this second option might need manual removal between tests
128
+ ```
129
+
130
+ ## Cache Issues
131
+
132
+ ### Stale Data Not Updating
133
+
134
+ **Problem:** UI doesn't reflect mutations or other updates.
135
+
136
+ **Solution 1:** Verify cache key identification:
137
+
138
+ ```typescript
139
+ const cache = new InMemoryCache({
140
+ typePolicies: {
141
+ // Ensure proper identification
142
+ Product: {
143
+ keyFields: ["id"], // or ['sku'] if no id field
144
+ },
145
+ },
146
+ });
147
+ ```
148
+
149
+ **Solution 2:** Update cache after mutations:
150
+
151
+ ```tsx
152
+ const [deleteProduct] = useMutation(DELETE_PRODUCT, {
153
+ update: (cache, { data }) => {
154
+ cache.evict({ id: cache.identify(data.deleteProduct) });
155
+ cache.gc();
156
+ },
157
+ });
158
+ ```
159
+
160
+ **Solution 3:** Use appropriate fetch policy:
161
+
162
+ ```tsx
163
+ const { data } = useQuery(GET_PRODUCTS, {
164
+ fetchPolicy: "cache-and-network", // Always fetch fresh data
165
+ });
166
+ ```
167
+
168
+ ### Missing Cache Updates After Mutation
169
+
170
+ **Problem:** New items don't appear in lists after creation.
171
+
172
+ **Solution:** Manually update the cache:
173
+
174
+ ```tsx
175
+ const [createProduct] = useMutation(CREATE_PRODUCT, {
176
+ update: (cache, { data }) => {
177
+ const existing = cache.readQuery<{ products: Product[] }>({
178
+ query: GET_PRODUCTS,
179
+ });
180
+
181
+ cache.writeQuery({
182
+ query: GET_PRODUCTS,
183
+ data: {
184
+ products: [...(existing?.products ?? []), data.createProduct],
185
+ },
186
+ });
187
+ },
188
+ });
189
+ ```
190
+
191
+ ### Pagination Cache Issues
192
+
193
+ **Problem:** Paginated data not merging correctly.
194
+
195
+ **Solution:** Configure proper type policies:
196
+
197
+ ```typescript
198
+ const cache = new InMemoryCache({
199
+ typePolicies: {
200
+ Query: {
201
+ fields: {
202
+ products: {
203
+ keyArgs: ["category"], // Only category creates new cache entries
204
+ merge(existing = [], incoming) {
205
+ return [...existing, ...incoming];
206
+ },
207
+ },
208
+ },
209
+ },
210
+ },
211
+ });
212
+ ```
213
+
214
+ ### Cache Normalization Problems
215
+
216
+ **Problem:** Objects with same ID showing different data in different queries.
217
+
218
+ **Debug:** Check cache contents:
219
+
220
+ ```typescript
221
+ // In DevTools console or component
222
+ console.log(client.cache.extract());
223
+ ```
224
+
225
+ **Solution:** Ensure consistent `__typename` and `id` fields:
226
+
227
+ ```graphql
228
+ query GetUsers {
229
+ users {
230
+ id # Always include id
231
+ name
232
+ }
233
+ }
234
+ ```
235
+
236
+ ## TypeScript Issues
237
+
238
+ ### Type Generation Setup
239
+
240
+ **Problem:** No type safety for GraphQL operations.
241
+
242
+ **Solution:** Set up GraphQL Code Generator with the [recommended starter configuration](https://www.apollographql.com/docs/react/development-testing/graphql-codegen#recommended-starter-configuration), as described in the [Skill](../SKILL.md).
243
+
244
+ ### Using Generated Types
245
+
246
+ ```tsx
247
+ import { useQuery } from "@apollo/client/react";
248
+ import { GetUsersDocument, GetUsersQuery } from "./generated/graphql";
249
+
250
+ function UserList() {
251
+ // Fully typed without manual type annotations
252
+ const { data, loading, error } = useQuery(GetUsersDocument);
253
+
254
+ // data.users is automatically typed as GetUsersQuery['users']
255
+ return (
256
+ <ul>{data?.users.map((user) => <li key={user.id}>{user.name}</li>)}</ul>
257
+ );
258
+ }
259
+ ```
260
+
261
+ ## Performance Issues
262
+
263
+ ### Over-Fetching
264
+
265
+ **Problem:** Fetching more data than needed.
266
+
267
+ **Solution:** Select only required fields:
268
+
269
+ ```graphql
270
+ # Bad - fetching everything
271
+ query GetUsers {
272
+ users {
273
+ id
274
+ name
275
+ email
276
+ profile { ... }
277
+ posts { ... }
278
+ friends { ... }
279
+ }
280
+ }
281
+
282
+ # Good - fetch what's needed
283
+ query GetUserNames {
284
+ users {
285
+ id
286
+ name
287
+ }
288
+ }
289
+ ```
290
+
291
+ ### N+1 Queries
292
+
293
+ **Problem:** Multiple network requests for related data.
294
+
295
+ **Solution:** Structure queries to batch requests. Best practice: use query colocation and compose queries from fragments defined on child components.
296
+
297
+ ```graphql
298
+ # Bad - separate queries
299
+ query GetUser($id: ID!) {
300
+ user(id: $id) {
301
+ id
302
+ name
303
+ }
304
+ }
305
+ query GetUserPosts($userId: ID!) {
306
+ posts(userId: $userId) {
307
+ id
308
+ title
309
+ }
310
+ }
311
+
312
+ # Good - single query
313
+ query GetUserWithPosts($id: ID!) {
314
+ user(id: $id) {
315
+ id
316
+ name
317
+ posts {
318
+ id
319
+ title
320
+ }
321
+ }
322
+ }
323
+ ```
324
+
325
+ ### Unnecessary Re-Renders
326
+
327
+ **Problem:** Components re-render when unrelated cache data changes.
328
+
329
+ **Solution:** Use `useFragment` and data masking for selective field reading. If that is not possible, `useQuery` with `@nonreactive` directives might be an alternative.
330
+
331
+ ```tsx
332
+ // Prefer useFragment with data masking
333
+ const { data } = useFragment({
334
+ fragment: USER_FRAGMENT,
335
+ from: { __typename: "User", id },
336
+ });
337
+
338
+ // Alternative: use @nonreactive directive
339
+ const GET_USER = gql`
340
+ query GetUser($id: ID!) {
341
+ user(id: $id) {
342
+ id
343
+ name
344
+ # This field won't trigger re-renders when it changes
345
+ metadata @nonreactive {
346
+ lastSeen
347
+ preferences
348
+ }
349
+ }
350
+ }
351
+ `;
352
+
353
+ const { data } = useQuery(GET_USER, {
354
+ variables: { id },
355
+ });
356
+ ```
357
+
358
+ ### Cache Misses
359
+
360
+ **Debug:** Use Apollo DevTools to inspect cache.
361
+
362
+ ```typescript
363
+ const client = new ApolloClient({
364
+ cache: new InMemoryCache(),
365
+ // DevTools are enabled by default in development
366
+ // Only configure this when you need to enable them in production
367
+ devtools: {
368
+ enabled: true,
369
+ },
370
+ });
371
+ ```
372
+
373
+ ## DevTools Usage
374
+
375
+ ### Installing Apollo DevTools
376
+
377
+ Install the browser extension:
378
+
379
+ - [Chrome](https://chrome.google.com/webstore/detail/apollo-client-devtools/jdkknkkbebbapilgoeccciglkfbmbnfm)
380
+ - [Firefox](https://addons.mozilla.org/en-US/firefox/addon/apollo-developer-tools/)
381
+
382
+ ### Enabling DevTools
383
+
384
+ DevTools are enabled by default in development. Only configure this setting if you need to enable them in production:
385
+
386
+ ```typescript
387
+ const client = new ApolloClient({
388
+ cache: new InMemoryCache(),
389
+ devtools: {
390
+ enabled: true, // Set to true to enable in production
391
+ },
392
+ });
393
+ ```
394
+
395
+ ### DevTools Features
396
+
397
+ 1. **Cache Inspector**: View normalized cache contents
398
+ 2. **Queries**: See active queries and their states
399
+ 3. **Mutations**: Track mutation history
400
+ 4. **Explorer**: Build and test queries against your schema
401
+ 5. **Memoization Limits**: Monitor and track cache memoization
402
+ 6. **Cache Writes**: Track all writes to the cache
403
+
404
+ ### Debugging Cache
405
+
406
+ ```typescript
407
+ // Log cache contents
408
+ console.log(JSON.stringify(client.cache.extract(), null, 2));
409
+
410
+ // Check specific object using cache.identify
411
+ console.log(
412
+ client.cache.readFragment({
413
+ id: cache.identify({ __typename: "User", id: 1 }),
414
+ fragment: gql`
415
+ fragment _ on User {
416
+ id
417
+ name
418
+ email
419
+ }
420
+ `,
421
+ })
422
+ );
423
+ ```
424
+
425
+ ## Common Error Messages
426
+
427
+ ### "Missing field 'X' in {...}"
428
+
429
+ **Cause:** Query doesn't include required field for cache normalization.
430
+
431
+ **Solution:** Include `id` and `__typename`:
432
+
433
+ ```graphql
434
+ query GetUsers {
435
+ users {
436
+ id # Required for caching
437
+ __typename # Usually added automatically
438
+ name
439
+ }
440
+ }
441
+ ```
442
+
443
+ **Additional advice**: Read the full error message thoroughly.
444
+
445
+ ### "Store reset while query was in flight"
446
+
447
+ **Cause:** `client.resetStore()` called during active queries.
448
+
449
+ **Solution:** Wait for queries to complete or use `clearStore()`:
450
+
451
+ ```typescript
452
+ // Option 1: Clear without refetching
453
+ await client.clearStore();
454
+
455
+ // Option 2: Reset and refetch active queries
456
+ await client.resetStore();
457
+ ```
458
+
459
+ ### "Invariant Violation: X"
460
+
461
+ **Cause:** Various configuration or usage errors.
462
+
463
+ **Common fixes:**
464
+
465
+ - Ensure `ApolloProvider` wraps the component tree
466
+ - Check that `gql` tagged templates are valid GraphQL
467
+ - Verify cache configuration matches your schema
468
+
469
+ ### "Cannot read property 'X' of undefined"
470
+
471
+ **Cause:** Accessing data before query completes.
472
+
473
+ **Solution:** Check `dataState` for proper type narrowing:
474
+
475
+ ```tsx
476
+ const { data, dataState } = useQuery(GET_USER);
477
+
478
+ // dataState can be "complete", "partial", "streaming", or "empty"
479
+ // It describes the completeness of the data, not a loading state
480
+ if (dataState === "empty") return <Spinner />;
481
+
482
+ // Now data is guaranteed to exist
483
+ return <div>{data.user.name}</div>;
484
+
485
+ // Or use optional chaining
486
+ return <div>{data?.user?.name}</div>;
487
+ ```
@@ -0,0 +1,133 @@
1
+ # TypeScript Code Generation
2
+
3
+ This guide covers setting up GraphQL Code Generator for type-safe Apollo Client usage with TypeScript.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install -D @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-operations @graphql-codegen/typed-document-node
9
+ ```
10
+
11
+ ## Configuration
12
+
13
+ Create a `codegen.ts` file in your project root:
14
+
15
+ ```typescript
16
+ // codegen.ts
17
+ import { CodegenConfig } from "@graphql-codegen/cli";
18
+
19
+ const config: CodegenConfig = {
20
+ overwrite: true,
21
+ schema: "<URL_OF_YOUR_GRAPHQL_API>",
22
+ // This assumes that all your source files are in a top-level `src/` directory - you might need to adjust this to your file structure
23
+ documents: ["src/**/*.{ts,tsx}"],
24
+ // Don't exit with non-zero status when there are no documents
25
+ ignoreNoDocuments: true,
26
+ generates: {
27
+ // Use a path that works the best for the structure of your application
28
+ "./src/types/__generated__/graphql.ts": {
29
+ plugins: ["typescript", "typescript-operations", "typed-document-node"],
30
+ config: {
31
+ avoidOptionals: {
32
+ // Use `null` for nullable fields instead of optionals
33
+ field: true,
34
+ // Allow nullable input fields to remain unspecified
35
+ inputValue: false,
36
+ },
37
+ // Use `unknown` instead of `any` for unconfigured scalars
38
+ defaultScalarType: "unknown",
39
+ // Apollo Client always includes `__typename` fields
40
+ nonOptionalTypename: true,
41
+ // Apollo Client doesn't add the `__typename` field to root types so
42
+ // don't generate a type for the `__typename` for root operation types.
43
+ skipTypeNameForRoot: true,
44
+ },
45
+ },
46
+ },
47
+ };
48
+
49
+ export default config;
50
+ ```
51
+
52
+ ## Enable Data Masking
53
+
54
+ To enable data masking with GraphQL Code Generator, create a type declaration file to inform Apollo Client about the generated types:
55
+
56
+ ```typescript
57
+ // apollo-client.d.ts
58
+ import { GraphQLCodegenDataMasking } from "@apollo/client/masking";
59
+
60
+ declare module "@apollo/client" {
61
+ export interface TypeOverrides
62
+ extends GraphQLCodegenDataMasking.TypeOverrides {}
63
+ }
64
+ ```
65
+
66
+ ## Running Code Generation
67
+
68
+ Add a script to your `package.json`:
69
+
70
+ ```json
71
+ {
72
+ "scripts": {
73
+ "codegen": "graphql-codegen"
74
+ }
75
+ }
76
+ ```
77
+
78
+ Run code generation:
79
+
80
+ ```bash
81
+ npm run codegen
82
+ ```
83
+
84
+ ## Usage with Apollo Client
85
+
86
+ The typed-document-node plugin generates `TypedDocumentNode` types that Apollo Client hooks automatically infer.
87
+
88
+ ### Defining Operations
89
+
90
+ Define your operations inline with the `if (false)` pattern. This allows GraphQL Code Generator to detect and extract operations without executing the code at runtime (bundlers omit this dead code during minification):
91
+
92
+ ```typescript
93
+ import { gql } from "@apollo/client";
94
+
95
+ // This query will never be consumed in runtime code, so it is wrapped in `if (false)` so the bundler can omit it when bundling.
96
+ if (false) {
97
+ gql`
98
+ query GetUser($id: ID!) {
99
+ user(id: $id) {
100
+ id
101
+ name
102
+ email
103
+ }
104
+ }
105
+ `;
106
+ }
107
+ ```
108
+
109
+ ### Using Generated Types
110
+
111
+ After running `npm run codegen`, import the generated `TypedDocumentNode`:
112
+
113
+ ```typescript
114
+ import { useQuery } from "@apollo/client/react";
115
+ import { GetUserDocument } from "./queries.generated";
116
+
117
+ function UserProfile({ userId }: { userId: string }) {
118
+ // Types are automatically inferred from GetUserDocument
119
+ const { data } = useQuery(GetUserDocument, {
120
+ variables: { id: userId },
121
+ });
122
+
123
+ // ... other logic ...
124
+
125
+ return <div>{data?.user.name}</div>;
126
+ }
127
+ ```
128
+
129
+ ## Important Notes
130
+
131
+ - The typed-document-node plugin might have a bundle size tradeoff but can prevent inconsistencies and is best suited for usage with LLMs, so it is recommended for most applications.
132
+ - See the [GraphQL Code Generator documentation](https://www.apollographql.com/docs/react/development-testing/graphql-codegen#recommended-starter-configuration) for other recommended configuration patterns if required.
133
+ - Apollo Client hooks automatically infer types from `TypedDocumentNode` - never use manual generics like `useQuery<QueryType, VariablesType>()`.
package/version.js CHANGED
@@ -1,3 +1,3 @@
1
- export const version = "4.1.6";
1
+ export const version = "4.1.8";
2
2
  export const build = "esm";
3
3
  //# sourceMappingURL=version.js.map