@apollo/client 4.2.0-alpha.2 → 4.2.0-alpha.4
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.
- package/CHANGELOG.md +240 -0
- package/__cjs/cache/core/cache.cjs +1 -1
- package/__cjs/cache/inmemory/entityStore.cjs +3 -3
- package/__cjs/cache/inmemory/entityStore.cjs.map +1 -1
- package/__cjs/cache/inmemory/key-extractor.cjs +1 -1
- package/__cjs/cache/inmemory/policies.cjs +4 -4
- package/__cjs/cache/inmemory/readFromStore.cjs +2 -2
- package/__cjs/cache/inmemory/writeToStore.cjs +4 -4
- package/__cjs/core/ApolloClient.cjs +52 -20
- package/__cjs/core/ApolloClient.cjs.map +1 -1
- package/__cjs/core/ApolloClient.d.cts +159 -22
- package/__cjs/core/ObservableQuery.cjs +7 -7
- package/__cjs/core/ObservableQuery.cjs.map +1 -1
- package/__cjs/core/ObservableQuery.d.cts +20 -1
- package/__cjs/core/QueryManager.cjs +12 -12
- package/__cjs/core/QueryManager.cjs.map +1 -1
- package/__cjs/core/RefetchEventManager.cjs +134 -0
- package/__cjs/core/RefetchEventManager.cjs.map +1 -0
- package/__cjs/core/RefetchEventManager.d.cts +130 -0
- package/__cjs/core/index.cjs +7 -1
- package/__cjs/core/index.cjs.map +1 -1
- package/__cjs/core/index.d.cts +4 -1
- package/__cjs/core/refetchSources/onlineSource.cjs +10 -0
- package/__cjs/core/refetchSources/onlineSource.cjs.map +1 -0
- package/__cjs/core/refetchSources/onlineSource.d.cts +3 -0
- package/__cjs/core/refetchSources/windowFocusSource.cjs +13 -0
- package/__cjs/core/refetchSources/windowFocusSource.cjs.map +1 -0
- package/__cjs/core/refetchSources/windowFocusSource.d.cts +3 -0
- package/__cjs/core/types.d.cts +20 -0
- package/__cjs/invariantErrorCodes.cjs +69 -44
- package/__cjs/link/ws/index.cjs +9 -1
- package/__cjs/link/ws/index.cjs.map +1 -1
- package/__cjs/link/ws/index.d.cts +1 -1
- package/__cjs/react/hooks/useBackgroundQuery.cjs.map +1 -1
- package/__cjs/react/hooks/useBackgroundQuery.d.cts +1486 -66
- package/__cjs/react/hooks/useLazyQuery.cjs +1 -0
- package/__cjs/react/hooks/useLazyQuery.cjs.map +1 -1
- package/__cjs/react/hooks/useLazyQuery.d.cts +366 -40
- package/__cjs/react/hooks/useLoadableQuery.cjs.map +1 -1
- package/__cjs/react/hooks/useLoadableQuery.d.cts +512 -50
- package/__cjs/react/hooks/useMutation.cjs +5 -48
- package/__cjs/react/hooks/useMutation.cjs.map +1 -1
- package/__cjs/react/hooks/useMutation.d.cts +239 -130
- package/__cjs/react/hooks/useQuery.cjs.map +1 -1
- package/__cjs/react/hooks/useQuery.d.cts +590 -41
- package/__cjs/react/hooks/useSubscription.cjs +1 -1
- package/__cjs/react/hooks/useSubscription.cjs.map +1 -1
- package/__cjs/react/hooks/useSubscription.d.cts +2 -2
- package/__cjs/react/hooks/useSuspenseQuery.cjs.map +1 -1
- package/__cjs/react/hooks/useSuspenseQuery.d.cts +754 -46
- package/__cjs/react/internal/cache/QueryReference.cjs +1 -0
- package/__cjs/react/internal/cache/QueryReference.cjs.map +1 -1
- package/__cjs/react/internal/cache/QueryReference.d.cts +1 -1
- package/__cjs/react/query-preloader/createQueryPreloader.cjs.map +1 -1
- package/__cjs/react/query-preloader/createQueryPreloader.d.cts +20 -1
- package/__cjs/react/types/types.documentation.d.cts +19 -0
- package/__cjs/version.cjs +1 -1
- package/cache/core/cache.js +1 -1
- package/cache/inmemory/entityStore.js +3 -3
- package/cache/inmemory/entityStore.js.map +1 -1
- package/cache/inmemory/key-extractor.js +1 -1
- package/cache/inmemory/policies.js +4 -4
- package/cache/inmemory/readFromStore.js +2 -2
- package/cache/inmemory/writeToStore.js +4 -4
- package/core/ApolloClient.d.ts +159 -22
- package/core/ApolloClient.js +53 -21
- package/core/ApolloClient.js.map +1 -1
- package/core/ObservableQuery.d.ts +20 -1
- package/core/ObservableQuery.js +7 -7
- package/core/ObservableQuery.js.map +1 -1
- package/core/QueryManager.js +12 -12
- package/core/QueryManager.js.map +1 -1
- package/core/RefetchEventManager.d.ts +130 -0
- package/core/RefetchEventManager.js +126 -0
- package/core/RefetchEventManager.js.map +1 -0
- package/core/index.d.ts +4 -1
- package/core/index.js +3 -0
- package/core/index.js.map +1 -1
- package/core/refetchSources/onlineSource.d.ts +3 -0
- package/core/refetchSources/onlineSource.js +6 -0
- package/core/refetchSources/onlineSource.js.map +1 -0
- package/core/refetchSources/windowFocusSource.d.ts +3 -0
- package/core/refetchSources/windowFocusSource.js +9 -0
- package/core/refetchSources/windowFocusSource.js.map +1 -0
- package/core/types.d.ts +20 -0
- package/core/types.js.map +1 -1
- package/invariantErrorCodes.js +69 -44
- package/link/ws/index.d.ts +1 -1
- package/link/ws/index.js +9 -1
- package/link/ws/index.js.map +1 -1
- package/package.json +3 -7
- package/react/hooks/useBackgroundQuery.d.ts +1486 -66
- package/react/hooks/useBackgroundQuery.js.map +1 -1
- package/react/hooks/useLazyQuery.d.ts +366 -40
- package/react/hooks/useLazyQuery.js +1 -0
- package/react/hooks/useLazyQuery.js.map +1 -1
- package/react/hooks/useLoadableQuery.d.ts +512 -50
- package/react/hooks/useLoadableQuery.js.map +1 -1
- package/react/hooks/useMutation.d.ts +239 -130
- package/react/hooks/useMutation.js +5 -48
- package/react/hooks/useMutation.js.map +1 -1
- package/react/hooks/useQuery.d.ts +590 -41
- package/react/hooks/useQuery.js.map +1 -1
- package/react/hooks/useSubscription.d.ts +2 -2
- package/react/hooks/useSubscription.js +1 -1
- package/react/hooks/useSubscription.js.map +1 -1
- package/react/hooks/useSuspenseQuery.d.ts +754 -46
- package/react/hooks/useSuspenseQuery.js.map +1 -1
- package/react/hooks-compiled/useBackgroundQuery.d.ts +1486 -66
- package/react/hooks-compiled/useBackgroundQuery.js.map +1 -1
- package/react/hooks-compiled/useLazyQuery.d.ts +366 -40
- package/react/hooks-compiled/useLazyQuery.js +1 -0
- package/react/hooks-compiled/useLazyQuery.js.map +1 -1
- package/react/hooks-compiled/useLoadableQuery.d.ts +512 -50
- package/react/hooks-compiled/useLoadableQuery.js.map +1 -1
- package/react/hooks-compiled/useMutation.d.ts +239 -130
- package/react/hooks-compiled/useMutation.js +4 -47
- package/react/hooks-compiled/useMutation.js.map +1 -1
- package/react/hooks-compiled/useQuery.d.ts +590 -41
- package/react/hooks-compiled/useQuery.js.map +1 -1
- package/react/hooks-compiled/useSubscription.d.ts +2 -2
- package/react/hooks-compiled/useSubscription.js +1 -1
- package/react/hooks-compiled/useSubscription.js.map +1 -1
- package/react/hooks-compiled/useSuspenseQuery.d.ts +754 -46
- package/react/hooks-compiled/useSuspenseQuery.js.map +1 -1
- package/react/internal/cache/QueryReference.d.ts +1 -1
- package/react/internal/cache/QueryReference.js +1 -0
- package/react/internal/cache/QueryReference.js.map +1 -1
- package/react/query-preloader/createQueryPreloader.d.ts +20 -1
- package/react/query-preloader/createQueryPreloader.js.map +1 -1
- package/react/types/types.documentation.d.ts +19 -0
- package/react/types/types.documentation.js.map +1 -1
- package/skills/apollo-client/SKILL.md +168 -0
- package/skills/apollo-client/references/caching.md +560 -0
- package/skills/apollo-client/references/error-handling.md +350 -0
- package/skills/apollo-client/references/fragments.md +804 -0
- package/skills/apollo-client/references/integration-client.md +336 -0
- package/skills/apollo-client/references/integration-nextjs.md +325 -0
- package/skills/apollo-client/references/integration-react-router.md +256 -0
- package/skills/apollo-client/references/integration-tanstack-start.md +378 -0
- package/skills/apollo-client/references/mutations.md +549 -0
- package/skills/apollo-client/references/queries.md +416 -0
- package/skills/apollo-client/references/state-management.md +428 -0
- package/skills/apollo-client/references/suspense-hooks.md +773 -0
- package/skills/apollo-client/references/troubleshooting.md +487 -0
- package/skills/apollo-client/references/typescript-codegen.md +133 -0
- package/version.js +1 -1
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
# Apollo Client Integration with React Router Framework Mode
|
|
2
|
+
|
|
3
|
+
This guide covers integrating Apollo Client in a React Router 7 application with support for modern streaming SSR.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Install Apollo Client and the React Router integration package:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @apollo/client-integration-react-router @apollo/client graphql rxjs
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
> **TypeScript users:** For type-safe GraphQL operations, see the [TypeScript Code Generation guide](typescript-codegen.md).
|
|
14
|
+
|
|
15
|
+
## Setup
|
|
16
|
+
|
|
17
|
+
### Step 1: Create Apollo Configuration
|
|
18
|
+
|
|
19
|
+
Create an `app/apollo.ts` file that exports a `makeClient` function and an `apolloLoader`:
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { HttpLink, InMemoryCache } from "@apollo/client";
|
|
23
|
+
import {
|
|
24
|
+
createApolloLoaderHandler,
|
|
25
|
+
ApolloClient,
|
|
26
|
+
} from "@apollo/client-integration-react-router";
|
|
27
|
+
|
|
28
|
+
// `request` will be available on the server during SSR or in loaders, but not in the browser
|
|
29
|
+
export const makeClient = (request?: Request) => {
|
|
30
|
+
return new ApolloClient({
|
|
31
|
+
cache: new InMemoryCache(),
|
|
32
|
+
link: new HttpLink({ uri: "https://your-graphql-endpoint.com/graphql" }),
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const apolloLoader = createApolloLoaderHandler(makeClient);
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
> **Important:** `ApolloClient` must be imported from `@apollo/client-integration-react-router`, not from `@apollo/client`.
|
|
40
|
+
|
|
41
|
+
### Step 2: Reveal Entry Files
|
|
42
|
+
|
|
43
|
+
Run the following command to create the entry files if they don't exist:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npx react-router reveal
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
This will create `app/entry.client.tsx` and `app/entry.server.tsx`.
|
|
50
|
+
|
|
51
|
+
### Step 3: Configure Client Entry
|
|
52
|
+
|
|
53
|
+
Adjust `app/entry.client.tsx` to wrap your app in `ApolloProvider`:
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
import { makeClient } from "./apollo";
|
|
57
|
+
import { ApolloProvider } from "@apollo/client";
|
|
58
|
+
import { StrictMode, startTransition } from "react";
|
|
59
|
+
import { hydrateRoot } from "react-dom/client";
|
|
60
|
+
import { HydratedRouter } from "react-router/dom";
|
|
61
|
+
|
|
62
|
+
startTransition(() => {
|
|
63
|
+
const client = makeClient();
|
|
64
|
+
hydrateRoot(
|
|
65
|
+
document,
|
|
66
|
+
<StrictMode>
|
|
67
|
+
<ApolloProvider client={client}>
|
|
68
|
+
<HydratedRouter />
|
|
69
|
+
</ApolloProvider>
|
|
70
|
+
</StrictMode>
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Step 4: Configure Server Entry
|
|
76
|
+
|
|
77
|
+
Adjust `app/entry.server.tsx` to wrap your app in `ApolloProvider` during SSR:
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
import { makeClient } from "./apollo";
|
|
81
|
+
import { ApolloProvider } from "@apollo/client";
|
|
82
|
+
// ... other imports
|
|
83
|
+
|
|
84
|
+
export default function handleRequest(
|
|
85
|
+
request: Request,
|
|
86
|
+
responseStatusCode: number,
|
|
87
|
+
responseHeaders: Headers,
|
|
88
|
+
routerContext: EntryContext
|
|
89
|
+
) {
|
|
90
|
+
return new Promise((resolve, reject) => {
|
|
91
|
+
// ... existing code
|
|
92
|
+
|
|
93
|
+
const client = makeClient(request);
|
|
94
|
+
|
|
95
|
+
const { pipe, abort } = renderToPipeableStream(
|
|
96
|
+
<ApolloProvider client={client}>
|
|
97
|
+
<ServerRouter
|
|
98
|
+
context={routerContext}
|
|
99
|
+
url={request.url}
|
|
100
|
+
abortDelay={ABORT_DELAY}
|
|
101
|
+
/>
|
|
102
|
+
</ApolloProvider>,
|
|
103
|
+
{
|
|
104
|
+
[readyOption]() {
|
|
105
|
+
shellRendered = true;
|
|
106
|
+
// ... rest of the handler
|
|
107
|
+
},
|
|
108
|
+
// ... other options
|
|
109
|
+
}
|
|
110
|
+
);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Step 5: Add Hydration Helper
|
|
116
|
+
|
|
117
|
+
Add `<ApolloHydrationHelper>` to `app/root.tsx`:
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
import { ApolloHydrationHelper } from "@apollo/client-integration-react-router";
|
|
121
|
+
import { Links, Meta, Outlet, Scripts, ScrollRestoration } from "react-router";
|
|
122
|
+
|
|
123
|
+
export function Layout({ children }: { children: React.ReactNode }) {
|
|
124
|
+
return (
|
|
125
|
+
<html lang="en">
|
|
126
|
+
<head>
|
|
127
|
+
<meta charSet="utf-8" />
|
|
128
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
129
|
+
<Meta />
|
|
130
|
+
<Links />
|
|
131
|
+
</head>
|
|
132
|
+
<body>
|
|
133
|
+
<ApolloHydrationHelper>{children}</ApolloHydrationHelper>
|
|
134
|
+
<ScrollRestoration />
|
|
135
|
+
<Scripts />
|
|
136
|
+
</body>
|
|
137
|
+
</html>
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export default function App() {
|
|
142
|
+
return <Outlet />;
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Usage
|
|
147
|
+
|
|
148
|
+
### Using apolloLoader with useReadQuery
|
|
149
|
+
|
|
150
|
+
You can now use the `apolloLoader` function to create Apollo-enabled loaders for your routes:
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
import { gql } from "@apollo/client";
|
|
154
|
+
import { useReadQuery } from "@apollo/client/react";
|
|
155
|
+
import { useLoaderData } from "react-router";
|
|
156
|
+
import type { Route } from "./+types/my-route";
|
|
157
|
+
import type { TypedDocumentNode } from "@apollo/client";
|
|
158
|
+
import { apolloLoader } from "./apollo";
|
|
159
|
+
|
|
160
|
+
// TypedDocumentNode definition with types
|
|
161
|
+
const GET_USER: TypedDocumentNode<
|
|
162
|
+
{ user: { id: string; name: string; email: string } },
|
|
163
|
+
{ id: string }
|
|
164
|
+
> = gql`
|
|
165
|
+
query GetUser($id: ID!) {
|
|
166
|
+
user(id: $id) {
|
|
167
|
+
id
|
|
168
|
+
name
|
|
169
|
+
email
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
`;
|
|
173
|
+
|
|
174
|
+
export const loader = apolloLoader<Route.LoaderArgs>()(({ preloadQuery }) => {
|
|
175
|
+
const userQueryRef = preloadQuery(GET_USER, {
|
|
176
|
+
variables: { id: "1" },
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
userQueryRef,
|
|
181
|
+
};
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
export default function UserPage() {
|
|
185
|
+
const { userQueryRef } = useLoaderData<typeof loader>();
|
|
186
|
+
const { data } = useReadQuery(userQueryRef);
|
|
187
|
+
|
|
188
|
+
return (
|
|
189
|
+
<div>
|
|
190
|
+
<h1>{data.user.name}</h1>
|
|
191
|
+
<p>{data.user.email}</p>
|
|
192
|
+
</div>
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
> **Important:** To provide better TypeScript support, `apolloLoader` is a method that you need to call twice: `apolloLoader<LoaderArgs>()(loader)`
|
|
198
|
+
|
|
199
|
+
### Multiple Queries in a Loader
|
|
200
|
+
|
|
201
|
+
You can preload multiple queries in a single loader:
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
import { gql } from "@apollo/client";
|
|
205
|
+
import { useReadQuery } from "@apollo/client/react";
|
|
206
|
+
import { useLoaderData } from "react-router";
|
|
207
|
+
import type { Route } from "./+types/my-route";
|
|
208
|
+
import { apolloLoader } from "./apollo";
|
|
209
|
+
|
|
210
|
+
// TypedDocumentNode definitions omitted for brevity
|
|
211
|
+
|
|
212
|
+
export const loader = apolloLoader<Route.LoaderArgs>()(({ preloadQuery }) => {
|
|
213
|
+
const userQueryRef = preloadQuery(GET_USER, {
|
|
214
|
+
variables: { id: "1" },
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
const postsQueryRef = preloadQuery(GET_POSTS, {
|
|
218
|
+
variables: { userId: "1" },
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
userQueryRef,
|
|
223
|
+
postsQueryRef,
|
|
224
|
+
};
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
export default function UserPage() {
|
|
228
|
+
const { userQueryRef, postsQueryRef } = useLoaderData<typeof loader>();
|
|
229
|
+
const { data: userData } = useReadQuery(userQueryRef);
|
|
230
|
+
const { data: postsData } = useReadQuery(postsQueryRef);
|
|
231
|
+
|
|
232
|
+
return (
|
|
233
|
+
<div>
|
|
234
|
+
<h1>{userData.user.name}</h1>
|
|
235
|
+
<h2>Posts</h2>
|
|
236
|
+
<ul>
|
|
237
|
+
{postsData.posts.map((post) => (
|
|
238
|
+
<li key={post.id}>{post.title}</li>
|
|
239
|
+
))}
|
|
240
|
+
</ul>
|
|
241
|
+
</div>
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Important Considerations
|
|
247
|
+
|
|
248
|
+
1. **Import ApolloClient from Integration Package:** Always import `ApolloClient` from `@apollo/client-integration-react-router`, not from `@apollo/client`, to ensure proper SSR hydration.
|
|
249
|
+
|
|
250
|
+
2. **TypeScript Support:** The `apolloLoader` function requires double invocation for proper TypeScript type inference: `apolloLoader<LoaderArgs>()(loader)`.
|
|
251
|
+
|
|
252
|
+
3. **Request Context:** The `makeClient` function receives the `Request` object during SSR and in loaders, but not in the browser. Use this to set up auth headers or other request-specific configuration.
|
|
253
|
+
|
|
254
|
+
4. **Streaming SSR:** The integration fully supports React's streaming SSR capabilities. Place `Suspense` boundaries strategically for optimal user experience.
|
|
255
|
+
|
|
256
|
+
5. **Cache Hydration:** The `ApolloHydrationHelper` component ensures that data loaded on the server is properly hydrated on the client, preventing unnecessary refetches.
|
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
# Apollo Client Integration with TanStack Start
|
|
2
|
+
|
|
3
|
+
This guide covers integrating Apollo Client in a TanStack Start application with support for modern streaming SSR.
|
|
4
|
+
|
|
5
|
+
> **Note:** When using `npx create-tsrouter-app` to create a new TanStack Start application, you can choose Apollo Client in the setup wizard to have all of this configuration automatically set up for you.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Install Apollo Client and the TanStack Start integration package:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @apollo/client-integration-tanstack-start @apollo/client graphql rxjs
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
> **TypeScript users:** For type-safe GraphQL operations, see the [TypeScript Code Generation guide](typescript-codegen.md).
|
|
16
|
+
|
|
17
|
+
## Setup
|
|
18
|
+
|
|
19
|
+
### Step 1: Configure Root Route with Context
|
|
20
|
+
|
|
21
|
+
In your `routes/__root.tsx`, change from `createRootRoute` to `createRootRouteWithContext` to provide the right context type:
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import type { ApolloClientIntegration } from "@apollo/client-integration-tanstack-start";
|
|
25
|
+
import {
|
|
26
|
+
createRootRouteWithContext,
|
|
27
|
+
Outlet,
|
|
28
|
+
} from "@tanstack/react-router";
|
|
29
|
+
|
|
30
|
+
export const Route = createRootRouteWithContext<ApolloClientIntegration.RouterContext>()({
|
|
31
|
+
component: RootComponent,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
function RootComponent() {
|
|
35
|
+
return (
|
|
36
|
+
<html>
|
|
37
|
+
<head>
|
|
38
|
+
<meta charSet="UTF-8" />
|
|
39
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
40
|
+
<title>My App</title>
|
|
41
|
+
</head>
|
|
42
|
+
<body>
|
|
43
|
+
<Outlet />
|
|
44
|
+
</body>
|
|
45
|
+
</html>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Step 2: Set Up Apollo Client in Router
|
|
51
|
+
|
|
52
|
+
In your `router.tsx`, set up your Apollo Client instance and run `routerWithApolloClient`:
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import {
|
|
56
|
+
routerWithApolloClient,
|
|
57
|
+
ApolloClient,
|
|
58
|
+
InMemoryCache,
|
|
59
|
+
} from "@apollo/client-integration-tanstack-start";
|
|
60
|
+
import { HttpLink } from "@apollo/client";
|
|
61
|
+
import { createRouter } from "@tanstack/react-router";
|
|
62
|
+
import { routeTree } from "./routeTree.gen";
|
|
63
|
+
|
|
64
|
+
export function getRouter() {
|
|
65
|
+
const apolloClient = new ApolloClient({
|
|
66
|
+
cache: new InMemoryCache(),
|
|
67
|
+
link: new HttpLink({ uri: "https://your-graphql-endpoint.com/graphql" }),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const router = createRouter({
|
|
71
|
+
routeTree,
|
|
72
|
+
context: {
|
|
73
|
+
...routerWithApolloClient.defaultContext,
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
return routerWithApolloClient(router, apolloClient);
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
> **Important:** `ApolloClient` and `InMemoryCache` must be imported from `@apollo/client-integration-tanstack-start`, not from `@apollo/client`.
|
|
82
|
+
|
|
83
|
+
## Usage
|
|
84
|
+
|
|
85
|
+
### Option 1: Loader with preloadQuery and useReadQuery
|
|
86
|
+
|
|
87
|
+
Use the `preloadQuery` function in your route loader to preload data during navigation:
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
import { gql } from "@apollo/client";
|
|
91
|
+
import { useReadQuery } from "@apollo/client/react";
|
|
92
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
93
|
+
import type { TypedDocumentNode } from "@apollo/client";
|
|
94
|
+
|
|
95
|
+
// TypedDocumentNode definition with types
|
|
96
|
+
const GET_USER: TypedDocumentNode<
|
|
97
|
+
{ user: { id: string; name: string; email: string } },
|
|
98
|
+
{ id: string }
|
|
99
|
+
> = gql`
|
|
100
|
+
query GetUser($id: ID!) {
|
|
101
|
+
user(id: $id) {
|
|
102
|
+
id
|
|
103
|
+
name
|
|
104
|
+
email
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
`;
|
|
108
|
+
|
|
109
|
+
export const Route = createFileRoute("/user/$userId")({
|
|
110
|
+
component: RouteComponent,
|
|
111
|
+
loader: ({ context: { preloadQuery }, params }) => {
|
|
112
|
+
const queryRef = preloadQuery(GET_USER, {
|
|
113
|
+
variables: { id: params.userId },
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
queryRef,
|
|
118
|
+
};
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
function RouteComponent() {
|
|
123
|
+
const { queryRef } = Route.useLoaderData();
|
|
124
|
+
const { data } = useReadQuery(queryRef);
|
|
125
|
+
|
|
126
|
+
return (
|
|
127
|
+
<div>
|
|
128
|
+
<h1>{data.user.name}</h1>
|
|
129
|
+
<p>{data.user.email}</p>
|
|
130
|
+
</div>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Option 2: Direct useSuspenseQuery in Component
|
|
136
|
+
|
|
137
|
+
You can also use Apollo Client's suspenseful hooks directly in your component without a loader:
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
import { gql } from "@apollo/client";
|
|
141
|
+
import { useSuspenseQuery } from "@apollo/client/react";
|
|
142
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
143
|
+
import type { TypedDocumentNode } from "@apollo/client";
|
|
144
|
+
|
|
145
|
+
// TypedDocumentNode definition with types
|
|
146
|
+
const GET_POSTS: TypedDocumentNode<{
|
|
147
|
+
posts: Array<{ id: string; title: string; content: string }>;
|
|
148
|
+
}> = gql`
|
|
149
|
+
query GetPosts {
|
|
150
|
+
posts {
|
|
151
|
+
id
|
|
152
|
+
title
|
|
153
|
+
content
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
`;
|
|
157
|
+
|
|
158
|
+
export const Route = createFileRoute("/posts")({
|
|
159
|
+
component: RouteComponent,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
function RouteComponent() {
|
|
163
|
+
const { data } = useSuspenseQuery(GET_POSTS);
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
<div>
|
|
167
|
+
<h1>Posts</h1>
|
|
168
|
+
<ul>
|
|
169
|
+
{data.posts.map((post) => (
|
|
170
|
+
<li key={post.id}>
|
|
171
|
+
<h2>{post.title}</h2>
|
|
172
|
+
<p>{post.content}</p>
|
|
173
|
+
</li>
|
|
174
|
+
))}
|
|
175
|
+
</ul>
|
|
176
|
+
</div>
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Multiple Queries in a Loader
|
|
182
|
+
|
|
183
|
+
You can preload multiple queries in a single loader:
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
import { gql } from "@apollo/client";
|
|
187
|
+
import { useReadQuery } from "@apollo/client/react";
|
|
188
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
189
|
+
|
|
190
|
+
// TypedDocumentNode definitions omitted for brevity
|
|
191
|
+
|
|
192
|
+
export const Route = createFileRoute("/dashboard")({
|
|
193
|
+
component: RouteComponent,
|
|
194
|
+
loader: ({ context: { preloadQuery } }) => {
|
|
195
|
+
const userQueryRef = preloadQuery(GET_USER, {
|
|
196
|
+
variables: { id: "current" },
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const statsQueryRef = preloadQuery(GET_STATS, {
|
|
200
|
+
variables: { period: "month" },
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
userQueryRef,
|
|
205
|
+
statsQueryRef,
|
|
206
|
+
};
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
function RouteComponent() {
|
|
211
|
+
const { userQueryRef, statsQueryRef } = Route.useLoaderData();
|
|
212
|
+
const { data: userData } = useReadQuery(userQueryRef);
|
|
213
|
+
const { data: statsData } = useReadQuery(statsQueryRef);
|
|
214
|
+
|
|
215
|
+
return (
|
|
216
|
+
<div>
|
|
217
|
+
<h1>Welcome, {userData.user.name}</h1>
|
|
218
|
+
<div>
|
|
219
|
+
<h2>Monthly Stats</h2>
|
|
220
|
+
<p>Views: {statsData.stats.views}</p>
|
|
221
|
+
<p>Clicks: {statsData.stats.clicks}</p>
|
|
222
|
+
</div>
|
|
223
|
+
</div>
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Using useQueryRefHandlers for Refetching
|
|
229
|
+
|
|
230
|
+
When using `useReadQuery`, you can get refetch functionality from `useQueryRefHandlers`:
|
|
231
|
+
|
|
232
|
+
> **Important:** Always call `useQueryRefHandlers` before `useReadQuery`. These two hooks interact with the same `queryRef`, and calling them in the wrong order could cause subtle bugs.
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
import { useReadQuery, useQueryRefHandlers, QueryRef } from "@apollo/client/react";
|
|
236
|
+
|
|
237
|
+
function UserComponent({ queryRef }: { queryRef: QueryRef<GetUserQuery> }) {
|
|
238
|
+
const { refetch } = useQueryRefHandlers(queryRef);
|
|
239
|
+
const { data } = useReadQuery(queryRef);
|
|
240
|
+
|
|
241
|
+
return (
|
|
242
|
+
<div>
|
|
243
|
+
<h1>{data.user.name}</h1>
|
|
244
|
+
<button onClick={() => refetch()}>Refresh</button>
|
|
245
|
+
</div>
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## Important Considerations
|
|
251
|
+
|
|
252
|
+
1. **Import from Integration Package:** Always import `ApolloClient` and `InMemoryCache` from `@apollo/client-integration-tanstack-start`, not from `@apollo/client`, to ensure proper SSR hydration.
|
|
253
|
+
|
|
254
|
+
2. **Context Type:** Use `createRootRouteWithContext<ApolloClientIntegration.RouterContext>()` to provide proper TypeScript types for the `preloadQuery` function in loaders.
|
|
255
|
+
|
|
256
|
+
3. **Loader vs Component Queries:**
|
|
257
|
+
|
|
258
|
+
- Use `preloadQuery` in loaders when you want to start fetching data before the component renders
|
|
259
|
+
- Use `useSuspenseQuery` directly in components for simpler cases or when data fetching can wait until render
|
|
260
|
+
|
|
261
|
+
4. **Streaming SSR:** The integration fully supports React's streaming SSR capabilities. Place `Suspense` boundaries strategically for optimal user experience.
|
|
262
|
+
|
|
263
|
+
5. **Cache Management:** The Apollo Client instance is shared across all routes, so cache updates from one route will be reflected in all routes that use the same data.
|
|
264
|
+
|
|
265
|
+
6. **Authentication:** Use Apollo Client's `SetContextLink` for dynamic auth tokens.
|
|
266
|
+
|
|
267
|
+
## Advanced Configuration
|
|
268
|
+
|
|
269
|
+
### Adding Authentication
|
|
270
|
+
|
|
271
|
+
For authentication in TanStack Start with SSR support, you need to handle both server and client environments differently. Use `createIsomorphicFn` to provide environment-specific implementations:
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
import {
|
|
275
|
+
ApolloClient,
|
|
276
|
+
InMemoryCache,
|
|
277
|
+
routerWithApolloClient,
|
|
278
|
+
} from "@apollo/client-integration-tanstack-start";
|
|
279
|
+
import { ApolloLink, HttpLink } from "@apollo/client";
|
|
280
|
+
import { SetContextLink } from "@apollo/client/link/context";
|
|
281
|
+
import { createIsomorphicFn } from "@tanstack/react-start";
|
|
282
|
+
import { createRouter } from "@tanstack/react-router";
|
|
283
|
+
import { getSession, getCookie } from "@tanstack/react-start/server";
|
|
284
|
+
import { routeTree } from "./routeTree.gen";
|
|
285
|
+
|
|
286
|
+
// Create isomorphic link that uses different implementations per environment
|
|
287
|
+
const createAuthLink = createIsomorphicFn()
|
|
288
|
+
.server(() => {
|
|
289
|
+
// Server-only: Can access server-side functions like `getCookies`, `getCookie`, `getSession`, etc. exported from `"@tanstack/react-start/server"`
|
|
290
|
+
return new SetContextLink(async (prevContext) => {
|
|
291
|
+
return {
|
|
292
|
+
headers: {
|
|
293
|
+
...prevContext.headers,
|
|
294
|
+
authorization: getCookie("Authorization"),
|
|
295
|
+
},
|
|
296
|
+
};
|
|
297
|
+
});
|
|
298
|
+
})
|
|
299
|
+
.client(() => {
|
|
300
|
+
// Client-only: Can access `localStorage` or other browser APIs
|
|
301
|
+
return new SetContextLink((prevContext) => {
|
|
302
|
+
return {
|
|
303
|
+
headers: {
|
|
304
|
+
...prevContext.headers,
|
|
305
|
+
authorization: localStorage.getItem("authToken") ?? "",
|
|
306
|
+
},
|
|
307
|
+
};
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
export function getRouter() {
|
|
312
|
+
const httpLink = new HttpLink({
|
|
313
|
+
uri: "https://your-graphql-endpoint.com/graphql",
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
const apolloClient = new ApolloClient({
|
|
317
|
+
cache: new InMemoryCache(),
|
|
318
|
+
link: ApolloLink.from([createAuthLink(), httpLink]),
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
const router = createRouter({
|
|
322
|
+
routeTree,
|
|
323
|
+
context: {
|
|
324
|
+
...routerWithApolloClient.defaultContext,
|
|
325
|
+
},
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
return routerWithApolloClient(router, apolloClient);
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
> **Important:** The `getRouter` function is called both on the server and client, so it must not contain environment-specific code. Use `createIsomorphicFn` to provide different implementations:
|
|
333
|
+
>
|
|
334
|
+
> - **Server:** Can access server-only functions like `getSession`, `getCookies`, `getCookie` from `@tanstack/react-start/server` to access authentication information in request or session data
|
|
335
|
+
> - **Client:** Can use `localStorage` or other browser APIs to access auth tokens (if setting `credentials: "include"` is sufficient, try to prefer that over manually setting auth headers client-side)
|
|
336
|
+
>
|
|
337
|
+
> This ensures your authentication works correctly in both SSR and browser contexts.
|
|
338
|
+
|
|
339
|
+
### Custom Cache Configuration
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
import {
|
|
343
|
+
ApolloClient,
|
|
344
|
+
InMemoryCache,
|
|
345
|
+
} from "@apollo/client-integration-tanstack-start";
|
|
346
|
+
import { HttpLink } from "@apollo/client";
|
|
347
|
+
import { createRouter } from "@tanstack/react-router";
|
|
348
|
+
import { routeTree } from "./routeTree.gen";
|
|
349
|
+
import { routerWithApolloClient } from "@apollo/client-integration-tanstack-start";
|
|
350
|
+
|
|
351
|
+
export function getRouter() {
|
|
352
|
+
const apolloClient = new ApolloClient({
|
|
353
|
+
cache: new InMemoryCache({
|
|
354
|
+
typePolicies: {
|
|
355
|
+
Query: {
|
|
356
|
+
fields: {
|
|
357
|
+
posts: {
|
|
358
|
+
merge(existing = [], incoming) {
|
|
359
|
+
return [...existing, ...incoming];
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
},
|
|
363
|
+
},
|
|
364
|
+
},
|
|
365
|
+
}),
|
|
366
|
+
link: new HttpLink({ uri: "https://your-graphql-endpoint.com/graphql" }),
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
const router = createRouter({
|
|
370
|
+
routeTree,
|
|
371
|
+
context: {
|
|
372
|
+
...routerWithApolloClient.defaultContext,
|
|
373
|
+
},
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
return routerWithApolloClient(router, apolloClient);
|
|
377
|
+
}
|
|
378
|
+
```
|