@aws/nx-plugin-mcp 0.120.0 → 0.121.0
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/bin/aws-nx-mcp.js +23 -35
- package/docs/guides/astro-docs.mdx +142 -0
- package/docs/guides/connection/py-agent-a2a.mdx +105 -0
- package/docs/guides/connection/py-agent-mcp.mdx +147 -0
- package/docs/guides/connection/react-agui.mdx +240 -0
- package/docs/guides/connection/react-fastapi.mdx +1476 -0
- package/docs/guides/connection/react-py-agent.mdx +198 -0
- package/docs/guides/connection/react-smithy.mdx +1355 -0
- package/docs/guides/connection/react-trpc.mdx +447 -0
- package/docs/guides/connection/react-ts-agent.mdx +198 -0
- package/docs/guides/connection/smithy-rdb.mdx +161 -0
- package/docs/guides/connection/trpc-rdb.mdx +127 -0
- package/docs/guides/connection/ts-agent-a2a.mdx +106 -0
- package/docs/guides/connection/ts-agent-mcp.mdx +144 -0
- package/docs/guides/connection/ts-agent-rdb.mdx +141 -0
- package/docs/guides/connection/ts-mcp-server-rdb.mdx +135 -0
- package/docs/guides/connection.mdx +126 -0
- package/docs/guides/docker-bundling.mdx +403 -0
- package/docs/guides/fastapi.mdx +509 -0
- package/docs/guides/license.mdx +293 -0
- package/docs/guides/nx-generator.mdx +588 -0
- package/docs/guides/py-agent.mdx +483 -0
- package/docs/guides/py-mcp-server.mdx +161 -0
- package/docs/guides/python-lambda-function.mdx +207 -0
- package/docs/guides/python-project.mdx +228 -0
- package/docs/guides/react-website-auth.mdx +216 -0
- package/docs/guides/react-website.mdx +421 -0
- package/docs/guides/runtime-config.mdx +312 -0
- package/docs/guides/terraform-project.mdx +317 -0
- package/docs/guides/trpc.mdx +816 -0
- package/docs/guides/ts-agent.mdx +436 -0
- package/docs/guides/ts-lambda-function.mdx +218 -0
- package/docs/guides/ts-mcp-server.mdx +169 -0
- package/docs/guides/ts-nx-plugin.mdx +159 -0
- package/docs/guides/ts-rdb.mdx +759 -0
- package/docs/guides/ts-smithy-api.mdx +661 -0
- package/docs/guides/typescript-infrastructure.mdx +408 -0
- package/docs/guides/typescript-project.mdx +312 -0
- package/docs/guides/workspace.mdx +181 -0
- package/docs/snippets/agent/architecture.mdx +72 -0
- package/docs/snippets/agent/bedrock-deployment.mdx +172 -0
- package/docs/snippets/agent/runtime-arn.mdx +64 -0
- package/docs/snippets/api/api-architecture.mdx +93 -0
- package/docs/snippets/api/api-choice-note.mdx +6 -0
- package/docs/snippets/api/cors-configuration-cdk-note.mdx +25 -0
- package/docs/snippets/api/cors-configuration-terraform-note.mdx +28 -0
- package/docs/snippets/api/shared-constructs.mdx +38 -0
- package/docs/snippets/api/type-safe-api-integrations.mdx +643 -0
- package/docs/snippets/api/waf-configuration.mdx +37 -0
- package/docs/snippets/connection/a2a-infrastructure.mdx +63 -0
- package/docs/snippets/connection/lambda-rdb-ssl-requirements.mdx +40 -0
- package/docs/snippets/connection/mcp-server-rdb-ssl-requirements.mdx +35 -0
- package/docs/snippets/connection/rdb-api-infrastructure.mdx +72 -0
- package/docs/snippets/connection/react-agent-infrastructure.mdx +61 -0
- package/docs/snippets/connection/strands-agent-rdb-ssl-requirements.mdx +35 -0
- package/docs/snippets/lambda-function/architecture.mdx +36 -0
- package/docs/snippets/lambda-function/deploying-your-function.mdx +118 -0
- package/docs/snippets/mcp/architecture.mdx +58 -0
- package/docs/snippets/mcp/assistant-docs.mdx +10 -0
- package/docs/snippets/mcp/bedrock-deployment.mdx +167 -0
- package/docs/snippets/mcp/config.mdx +13 -0
- package/docs/snippets/mcp/configuration-py.mdx +42 -0
- package/docs/snippets/mcp/configuration-ts.mdx +53 -0
- package/docs/snippets/mcp/observability.mdx +8 -0
- package/docs/snippets/mcp/shared-constructs.mdx +32 -0
- package/docs/snippets/pdk-migration/example/01-migrate-api.mdx +602 -0
- package/docs/snippets/pdk-migration/example/02-migrate-website.mdx +915 -0
- package/docs/snippets/pdk-migration/example/03-migrate-infra.mdx +161 -0
- package/docs/snippets/pdk-migration/example/04-deploy.mdx +229 -0
- package/docs/snippets/pdk-migration/faq/aws-arch.mdx +17 -0
- package/docs/snippets/pdk-migration/faq/cdk-graph.mdx +29 -0
- package/docs/snippets/pdk-migration/faq/infrastructure-python-java.mdx +19 -0
- package/docs/snippets/pdk-migration/faq/pdk-nag.mdx +15 -0
- package/docs/snippets/pdk-migration/faq/pipeline.mdx +15 -0
- package/docs/snippets/pdk-migration/faq/type-safe-api.mdx +310 -0
- package/docs/snippets/pdk-migration/faq/use-of-projen.mdx +15 -0
- package/docs/snippets/prerequisites.mdx +20 -0
- package/docs/snippets/required-prerequisites.mdx +12 -0
- package/docs/snippets/shared-constructs.mdx +40 -0
- package/docs/snippets/tools/acurl.mdx +73 -0
- package/docs/snippets/ts-bundle.mdx +14 -0
- package/package.json +1 -1
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: React to tRPC
|
|
3
|
+
description: Connect a React website to a tRPC API
|
|
4
|
+
when:
|
|
5
|
+
sourceType: react
|
|
6
|
+
targetType: ts#trpc-api
|
|
7
|
+
---
|
|
8
|
+
import { FileTree } from '@astrojs/starlight/components';
|
|
9
|
+
import Link from '@components/link.astro';
|
|
10
|
+
import RunGenerator from '@components/run-generator.astro';
|
|
11
|
+
import GeneratorParameters from '@components/generator-parameters.astro';
|
|
12
|
+
|
|
13
|
+
Nx Plugin for AWS provides a generator to quickly integrate your <Link path="guides/trpc">tRPC API</Link> with a React website. It sets up all necessary configuration for connecting to your tRPC backends, including AWS IAM and Cognito authentication support and proper error handling. The integration provides full end-to-end type safety between your frontend and tRPC backend(s).
|
|
14
|
+
|
|
15
|
+
## Prerequisites
|
|
16
|
+
|
|
17
|
+
Before using this generator, ensure your React application has:
|
|
18
|
+
|
|
19
|
+
1. A `main.tsx` file that renders your application
|
|
20
|
+
2. An `<App/>` JSX element where the tRPC provider will be automatically injected
|
|
21
|
+
3. A working tRPC API (generated using the tRPC API generator)
|
|
22
|
+
4. Cognito Auth added via the <Link path="/guides/react-website-auth">`ts#react-website-auth` generator</Link> if connecting an API which uses Cognito or IAM auth
|
|
23
|
+
|
|
24
|
+
<details>
|
|
25
|
+
<summary>Example of required `main.tsx` structure</summary>
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
import { StrictMode } from 'react';
|
|
29
|
+
import * as ReactDOM from 'react-dom/client';
|
|
30
|
+
import App from './app/app';
|
|
31
|
+
|
|
32
|
+
const root = ReactDOM.createRoot(
|
|
33
|
+
document.getElementById('root') as HTMLElement,
|
|
34
|
+
);
|
|
35
|
+
root.render(
|
|
36
|
+
<StrictMode>
|
|
37
|
+
<App />
|
|
38
|
+
</StrictMode>,
|
|
39
|
+
);
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
</details>
|
|
43
|
+
|
|
44
|
+
## Usage
|
|
45
|
+
|
|
46
|
+
### Run the Generator
|
|
47
|
+
|
|
48
|
+
<RunGenerator generator="connection" />
|
|
49
|
+
|
|
50
|
+
### Options
|
|
51
|
+
|
|
52
|
+
<GeneratorParameters generator="connection" />
|
|
53
|
+
|
|
54
|
+
## Generator Output
|
|
55
|
+
|
|
56
|
+
The generator creates the following structure in your React application:
|
|
57
|
+
|
|
58
|
+
<FileTree>
|
|
59
|
+
|
|
60
|
+
- src
|
|
61
|
+
- components
|
|
62
|
+
- \<ApiName>ClientProvider.tsx Sets up the tRPC clients and bindings to your backend schema(s). ApiName will resolve to the name of the API
|
|
63
|
+
- QueryClientProvider.tsx TanStack React Query client provider
|
|
64
|
+
- hooks
|
|
65
|
+
- useSigV4.tsx Hook for signing HTTP requests with SigV4 (IAM only)
|
|
66
|
+
- use\<ApiName>.tsx A hook returning the tRPC options proxy for TanStack Query integration
|
|
67
|
+
- use\<ApiName>Client.tsx A hook returning the vanilla tRPC client for direct API calls
|
|
68
|
+
|
|
69
|
+
</FileTree>
|
|
70
|
+
|
|
71
|
+
Additionally, it installs the required dependencies:
|
|
72
|
+
|
|
73
|
+
- `@trpc/client`
|
|
74
|
+
- `@trpc/tanstack-react-query`
|
|
75
|
+
- `@tanstack/react-query`
|
|
76
|
+
- `aws4fetch` (if using IAM auth)
|
|
77
|
+
- `event-source-polyfill` (if using REST API, for subscription support)
|
|
78
|
+
|
|
79
|
+
## Using the Generated Code
|
|
80
|
+
|
|
81
|
+
### Using the tRPC Options Proxy Hook
|
|
82
|
+
|
|
83
|
+
The generator provides a `use<ApiName>` hook that returns a [tRPC options proxy](https://trpc.io/docs/client/tanstack-react-query) for use with TanStack Query hooks like `useQuery` and `useMutation`:
|
|
84
|
+
|
|
85
|
+
```tsx {5,8,11}
|
|
86
|
+
import { useQuery, useMutation } from '@tanstack/react-query';
|
|
87
|
+
import { useMyApi } from './hooks/useMyApi';
|
|
88
|
+
|
|
89
|
+
function MyComponent() {
|
|
90
|
+
const trpc = useMyApi();
|
|
91
|
+
|
|
92
|
+
// Example query
|
|
93
|
+
const { data, isLoading, error } = useQuery(trpc.users.list.queryOptions());
|
|
94
|
+
|
|
95
|
+
// Example mutation
|
|
96
|
+
const mutation = useMutation(trpc.users.create.mutationOptions());
|
|
97
|
+
|
|
98
|
+
const handleCreate = () => {
|
|
99
|
+
mutation.mutate({
|
|
100
|
+
name: 'John Doe',
|
|
101
|
+
email: 'john@example.com',
|
|
102
|
+
});
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
if (isLoading) return <div>Loading...</div>;
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<ul>
|
|
109
|
+
{data.map((user) => (
|
|
110
|
+
<li key={user.id}>{user.name}</li>
|
|
111
|
+
))}
|
|
112
|
+
</ul>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Using the Vanilla tRPC Client
|
|
118
|
+
|
|
119
|
+
The `use<ApiName>Client` hook provides access to the [vanilla tRPC client](https://trpc.io/docs/client/vanilla), which is useful for imperative API calls and subscriptions:
|
|
120
|
+
|
|
121
|
+
```tsx {2,5,8,11}
|
|
122
|
+
import { useState } from 'react';
|
|
123
|
+
import { useMyApiClient } from './hooks/useMyApi';
|
|
124
|
+
|
|
125
|
+
function MyComponent() {
|
|
126
|
+
const client = useMyApiClient();
|
|
127
|
+
|
|
128
|
+
const handleClick = async () => {
|
|
129
|
+
const result = await client.echo.query({ message: 'Hello!' });
|
|
130
|
+
console.log(result);
|
|
131
|
+
|
|
132
|
+
const mutationResult = await client.users.create.mutate({ name: 'Jane' });
|
|
133
|
+
console.log(mutationResult);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
return <button onClick={handleClick}>Call API</button>;
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Error Handling
|
|
141
|
+
|
|
142
|
+
The integration includes built-in error handling that properly processes tRPC errors:
|
|
143
|
+
|
|
144
|
+
```tsx {4, 6}
|
|
145
|
+
function MyComponent() {
|
|
146
|
+
const trpc = useMyApi();
|
|
147
|
+
|
|
148
|
+
const { data, error } = useQuery(trpc.users.list.queryOptions());
|
|
149
|
+
|
|
150
|
+
if (error) {
|
|
151
|
+
return (
|
|
152
|
+
<div>
|
|
153
|
+
<h2>Error occurred:</h2>
|
|
154
|
+
<p>{error.message}</p>
|
|
155
|
+
{error.data?.code && <p>Code: {error.data.code}</p>}
|
|
156
|
+
</div>
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<ul>
|
|
162
|
+
{data.map((user) => (
|
|
163
|
+
<li key={user.id}>{user.name}</li>
|
|
164
|
+
))}
|
|
165
|
+
</ul>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Subscriptions (Streaming)
|
|
171
|
+
|
|
172
|
+
:::caution[Subscriptions Compute Type]
|
|
173
|
+
Subscriptions are only supported when the tRPC API uses `ServerlessApiGatewayRestApi` (REST API) as the compute type. API Gateway HTTP APIs do not support response streaming.
|
|
174
|
+
:::
|
|
175
|
+
|
|
176
|
+
When connecting to a REST API tRPC backend, the generated client is automatically configured with a `splitLink` that routes subscription operations through `httpSubscriptionLink` (using SSE) and regular queries/mutations through `httpLink`. This means subscriptions work out of the box with no additional configuration.
|
|
177
|
+
|
|
178
|
+
For information on how to define subscription procedures in your backend, see the <Link path="guides/trpc">`ts#trpc-api` generator guide</Link>.
|
|
179
|
+
|
|
180
|
+
#### Using the useSubscription Hook
|
|
181
|
+
|
|
182
|
+
You can consume subscriptions using the `useSubscription` hook with `subscriptionOptions` from the options proxy:
|
|
183
|
+
|
|
184
|
+
```tsx {1-2,7-22}
|
|
185
|
+
import { useSubscription } from '@trpc/tanstack-react-query';
|
|
186
|
+
import { useMyApi } from './hooks/useMyApi';
|
|
187
|
+
|
|
188
|
+
function StreamingComponent() {
|
|
189
|
+
const trpc = useMyApi();
|
|
190
|
+
|
|
191
|
+
const subscription = useSubscription(
|
|
192
|
+
trpc.myStream.subscriptionOptions(
|
|
193
|
+
{ query: 'hello' },
|
|
194
|
+
{
|
|
195
|
+
enabled: true,
|
|
196
|
+
onStarted: () => {
|
|
197
|
+
console.log('Subscription started');
|
|
198
|
+
},
|
|
199
|
+
onData: (data) => {
|
|
200
|
+
console.log('Received:', data.text);
|
|
201
|
+
},
|
|
202
|
+
onError: (error) => {
|
|
203
|
+
console.error('Subscription error:', error);
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
),
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
return (
|
|
210
|
+
<div>
|
|
211
|
+
<p>Status: {subscription.status}</p>
|
|
212
|
+
{subscription.data && <p>Latest: {subscription.data.text}</p>}
|
|
213
|
+
{subscription.error && <p>Error: {subscription.error.message}</p>}
|
|
214
|
+
<button onClick={() => subscription.reset()}>Reset</button>
|
|
215
|
+
</div>
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
The `subscription` object provides:
|
|
221
|
+
|
|
222
|
+
- `subscription.data` — the most recently received data
|
|
223
|
+
- `subscription.error` — the most recently received error
|
|
224
|
+
- `subscription.status` — one of `'idle'`, `'connecting'`, `'pending'`, or `'error'`
|
|
225
|
+
- `subscription.reset()` — resets the subscription (useful for recovering from errors)
|
|
226
|
+
|
|
227
|
+
#### Using the Vanilla Client
|
|
228
|
+
|
|
229
|
+
Alternatively, you can use the vanilla tRPC client via the `use<ApiName>Client` hook for more control over the subscription lifecycle:
|
|
230
|
+
|
|
231
|
+
```tsx {2,5,9-22,25}
|
|
232
|
+
import { useState, useEffect } from 'react';
|
|
233
|
+
import { useMyApiClient } from './hooks/useMyApi';
|
|
234
|
+
|
|
235
|
+
function StreamingComponent() {
|
|
236
|
+
const client = useMyApiClient();
|
|
237
|
+
const [messages, setMessages] = useState<string[]>([]);
|
|
238
|
+
|
|
239
|
+
useEffect(() => {
|
|
240
|
+
const subscription = client.myStream.subscribe(
|
|
241
|
+
{ query: 'hello' },
|
|
242
|
+
{
|
|
243
|
+
onData: (data) => {
|
|
244
|
+
setMessages((prev) => [...prev, data.text]);
|
|
245
|
+
},
|
|
246
|
+
onComplete: () => {
|
|
247
|
+
console.log('Stream complete');
|
|
248
|
+
},
|
|
249
|
+
onError: (error) => {
|
|
250
|
+
console.error('Stream error:', error);
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
// Clean up the subscription on unmount
|
|
256
|
+
return () => subscription.unsubscribe();
|
|
257
|
+
}, [client]);
|
|
258
|
+
|
|
259
|
+
return (
|
|
260
|
+
<ul>
|
|
261
|
+
{messages.map((msg, i) => (
|
|
262
|
+
<li key={i}>{msg}</li>
|
|
263
|
+
))}
|
|
264
|
+
</ul>
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## Best Practices
|
|
270
|
+
|
|
271
|
+
### Handle Loading States
|
|
272
|
+
|
|
273
|
+
Always handle loading and error states for a better user experience:
|
|
274
|
+
|
|
275
|
+
```tsx {6}
|
|
276
|
+
function UserList() {
|
|
277
|
+
const trpc = useMyApi();
|
|
278
|
+
|
|
279
|
+
const users = useQuery(trpc.users.list.queryOptions());
|
|
280
|
+
|
|
281
|
+
if (users.isLoading) {
|
|
282
|
+
return <LoadingSpinner />;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (users.error) {
|
|
286
|
+
return <ErrorMessage error={users.error} />;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return (
|
|
290
|
+
<ul>
|
|
291
|
+
{users.data.map((user) => (
|
|
292
|
+
<li key={user.id}>{user.name}</li>
|
|
293
|
+
))}
|
|
294
|
+
</ul>
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Optimistic Updates
|
|
300
|
+
|
|
301
|
+
Use optimistic updates for a better user experience:
|
|
302
|
+
|
|
303
|
+
```tsx {15-17,20-22,28-31}
|
|
304
|
+
import { useQueryClient, useQuery, useMutation } from '@tanstack/react-query';
|
|
305
|
+
|
|
306
|
+
function UserList() {
|
|
307
|
+
const trpc = useMyApi();
|
|
308
|
+
const users = useQuery(trpc.users.list.queryOptions());
|
|
309
|
+
const queryClient = useQueryClient();
|
|
310
|
+
|
|
311
|
+
const deleteMutation = useMutation(
|
|
312
|
+
trpc.users.delete.mutationOptions({
|
|
313
|
+
onMutate: async (userId) => {
|
|
314
|
+
// Cancel outgoing fetches
|
|
315
|
+
await queryClient.cancelQueries(trpc.users.list.queryFilter());
|
|
316
|
+
|
|
317
|
+
// Get snapshot of current data
|
|
318
|
+
const previousUsers = queryClient.getQueryData(
|
|
319
|
+
trpc.users.list.queryKey(),
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
// Optimistically remove the user
|
|
323
|
+
queryClient.setQueryData(trpc.users.list.queryKey(), (old) =>
|
|
324
|
+
old?.filter((user) => user.id !== userId),
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
return { previousUsers };
|
|
328
|
+
},
|
|
329
|
+
onError: (err, userId, context) => {
|
|
330
|
+
// Restore previous data on error
|
|
331
|
+
queryClient.setQueryData(
|
|
332
|
+
trpc.users.list.queryKey(),
|
|
333
|
+
context?.previousUsers,
|
|
334
|
+
);
|
|
335
|
+
},
|
|
336
|
+
}),
|
|
337
|
+
);
|
|
338
|
+
|
|
339
|
+
return (
|
|
340
|
+
<ul>
|
|
341
|
+
{users.map((user) => (
|
|
342
|
+
<li key={user.id}>
|
|
343
|
+
{user.name}
|
|
344
|
+
<button onClick={() => deleteMutation.mutate(user.id)}>Delete</button>
|
|
345
|
+
</li>
|
|
346
|
+
))}
|
|
347
|
+
</ul>
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### Prefetching Data
|
|
353
|
+
|
|
354
|
+
Prefetch data for better performance:
|
|
355
|
+
|
|
356
|
+
```tsx {8}
|
|
357
|
+
function UserList() {
|
|
358
|
+
const trpc = useMyApi();
|
|
359
|
+
const users = useQuery(trpc.users.list.queryOptions());
|
|
360
|
+
const queryClient = useQueryClient();
|
|
361
|
+
|
|
362
|
+
// Prefetch user details on hover
|
|
363
|
+
const prefetchUser = async (userId: string) => {
|
|
364
|
+
await queryClient.prefetchQuery(trpc.users.getById.queryOptions(userId));
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
return (
|
|
368
|
+
<ul>
|
|
369
|
+
{users.map((user) => (
|
|
370
|
+
<li key={user.id} onMouseEnter={() => prefetchUser(user.id)}>
|
|
371
|
+
<Link to={`/users/${user.id}`}>{user.name}</Link>
|
|
372
|
+
</li>
|
|
373
|
+
))}
|
|
374
|
+
</ul>
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### Infinite Queries
|
|
380
|
+
|
|
381
|
+
Handle pagination with infinite queries:
|
|
382
|
+
|
|
383
|
+
```tsx {5-12}
|
|
384
|
+
function UserList() {
|
|
385
|
+
const trpc = useMyApi();
|
|
386
|
+
|
|
387
|
+
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
|
|
388
|
+
useInfiniteQuery(
|
|
389
|
+
trpc.users.list.infiniteQueryOptions(
|
|
390
|
+
{ limit: 10 },
|
|
391
|
+
{
|
|
392
|
+
getNextPageParam: (lastPage) => lastPage.nextCursor,
|
|
393
|
+
},
|
|
394
|
+
),
|
|
395
|
+
);
|
|
396
|
+
|
|
397
|
+
return (
|
|
398
|
+
<div>
|
|
399
|
+
{data?.pages.map((page) =>
|
|
400
|
+
page.users.map((user) => <UserCard key={user.id} user={user} />),
|
|
401
|
+
)}
|
|
402
|
+
|
|
403
|
+
{hasNextPage && (
|
|
404
|
+
<button onClick={() => fetchNextPage()} disabled={isFetchingNextPage}>
|
|
405
|
+
{isFetchingNextPage ? 'Loading...' : 'Load More'}
|
|
406
|
+
</button>
|
|
407
|
+
)}
|
|
408
|
+
</div>
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
It is important to note that infinite queries can only be used for procedures with an input property named `cursor`.
|
|
414
|
+
|
|
415
|
+
## Type Safety
|
|
416
|
+
|
|
417
|
+
The integration provides complete end-to-end type safety. Your IDE will provide full autocompletion and type checking for all your API calls:
|
|
418
|
+
|
|
419
|
+
```tsx
|
|
420
|
+
function UserForm() {
|
|
421
|
+
const trpc = useMyApi();
|
|
422
|
+
|
|
423
|
+
// ✅ Input is fully typed
|
|
424
|
+
const createUser = trpc.users.create.useMutation();
|
|
425
|
+
|
|
426
|
+
const handleSubmit = (data: CreateUserInput) => {
|
|
427
|
+
// ✅ Type error if input doesn't match schema
|
|
428
|
+
createUser.mutate(data);
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
return <form onSubmit={handleSubmit}>{/* ... */}</form>;
|
|
432
|
+
}
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
The types are automatically inferred from your backend's router and schema definitions, ensuring that any changes to your API are immediately reflected in your frontend code without the need to build.
|
|
436
|
+
|
|
437
|
+
## Custom Auth
|
|
438
|
+
|
|
439
|
+
If your tRPC API uses `Custom` authentication (Lambda Authorizer), the generated client provider includes placeholder `headers` where you must add the authorization headers your authorizer expects. Look for the `// TODO: Add headers required by your custom authorizer` comments in the generated `<ApiName>ClientProvider.tsx` and replace them with your token or API key logic.
|
|
440
|
+
|
|
441
|
+
## More Information
|
|
442
|
+
|
|
443
|
+
For more information, please refer to:
|
|
444
|
+
|
|
445
|
+
- [tRPC TanStack React Query Integration](https://trpc.io/docs/client/tanstack-react-query)
|
|
446
|
+
- [tRPC Vanilla Client](https://trpc.io/docs/client/vanilla)
|
|
447
|
+
- [TanStack Query documentation](https://tanstack.com/query/v5)
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: React to TypeScript Agent
|
|
3
|
+
description: Connect a React website to a TypeScript Agent
|
|
4
|
+
when:
|
|
5
|
+
sourceType: react
|
|
6
|
+
targetType: ts#agent
|
|
7
|
+
protocol: HTTP
|
|
8
|
+
---
|
|
9
|
+
import { FileTree } from '@astrojs/starlight/components';
|
|
10
|
+
import Link from '@components/link.astro';
|
|
11
|
+
import RunGenerator from '@components/run-generator.astro';
|
|
12
|
+
import GeneratorParameters from '@components/generator-parameters.astro';
|
|
13
|
+
import Snippet from '@components/snippet.astro';
|
|
14
|
+
|
|
15
|
+
Nx Plugin for AWS provides a generator to quickly integrate your <Link path="guides/ts-agent">TypeScript Agent</Link> with a React website. It sets up all necessary configuration for connecting to your agent via tRPC over WebSocket, including AWS IAM and Cognito authentication support. The integration provides full end-to-end type safety between your frontend and the agent's tRPC router.
|
|
16
|
+
|
|
17
|
+
## Prerequisites
|
|
18
|
+
|
|
19
|
+
Before using this generator, ensure you have:
|
|
20
|
+
|
|
21
|
+
1. A React website (generated using the <Link path="guides/react-website">`ts#react-website` generator</Link>)
|
|
22
|
+
2. A TypeScript Agent (generated using the <Link path="guides/ts-agent">`ts#agent` generator</Link>)
|
|
23
|
+
3. Cognito Auth added via the <Link path="/guides/react-website-auth">`ts#react-website-auth` generator</Link>
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
### Run the Generator
|
|
28
|
+
|
|
29
|
+
<RunGenerator generator="connection" />
|
|
30
|
+
|
|
31
|
+
You will be prompted to select your React website as the source project and the project containing your Agent as the target project. If your target project contains multiple components (such as multiple agents or other component types), you will be prompted to specify a `targetComponent` to disambiguate.
|
|
32
|
+
|
|
33
|
+
### Options
|
|
34
|
+
|
|
35
|
+
<GeneratorParameters generator="connection" />
|
|
36
|
+
|
|
37
|
+
## Generator Output
|
|
38
|
+
|
|
39
|
+
The generator creates the following structure in your React application:
|
|
40
|
+
|
|
41
|
+
<FileTree>
|
|
42
|
+
|
|
43
|
+
- src
|
|
44
|
+
- components
|
|
45
|
+
- \<AgentName>AgentClientProvider.tsx Sets up the tRPC WebSocket client and bindings to your agent's tRPC router
|
|
46
|
+
- QueryClientProvider.tsx TanStack React Query client provider
|
|
47
|
+
- hooks
|
|
48
|
+
- useSigV4.tsx Hook for signing requests with SigV4 (IAM only)
|
|
49
|
+
- use\<AgentName>Agent.tsx Hooks returning the tRPC options proxy and vanilla tRPC client
|
|
50
|
+
|
|
51
|
+
</FileTree>
|
|
52
|
+
|
|
53
|
+
Additionally, it installs the required dependencies:
|
|
54
|
+
|
|
55
|
+
- `@trpc/client`
|
|
56
|
+
- `@trpc/tanstack-react-query`
|
|
57
|
+
- `@tanstack/react-query`
|
|
58
|
+
- `aws4fetch` (if using IAM auth)
|
|
59
|
+
|
|
60
|
+
## How It Works
|
|
61
|
+
|
|
62
|
+
### WebSocket Connection
|
|
63
|
+
|
|
64
|
+
The generated client connects to your Agent via tRPC over WebSocket. The agent exposes a tRPC router (including the `invoke` subscription for streaming agent responses) over a WebSocket endpoint.
|
|
65
|
+
|
|
66
|
+
- **Deployed**: The agent runtime ARN is loaded from <Link path="guides/runtime-config">Runtime Configuration</Link>. Running this connection generator also patches the agent's generated CDK/Terraform construct to publish its ARN to the website's `runtime-config.json` (under the `connection` namespace), so only agents you explicitly connect are exposed to the frontend. The ARN is converted to a WebSocket URL following the Bedrock AgentCore Runtime WebSocket protocol: `wss://bedrock-agentcore.<region>.amazonaws.com/runtimes/<encoded-arn>/ws`
|
|
67
|
+
- **Local development**: When running with `serve-local`, the runtime config override sets the value to a local `ws://` URL (e.g., `ws://localhost:8081/ws`), and the client connects directly
|
|
68
|
+
|
|
69
|
+
### Authentication
|
|
70
|
+
|
|
71
|
+
The generated code handles authentication depending on your agent's configuration:
|
|
72
|
+
|
|
73
|
+
- **IAM** (default): Uses AWS SigV4 presigned URLs to authenticate the WebSocket connection. Credentials are obtained from the Cognito Identity Pool configured with your website's auth. In `serve-local` mode, signing is automatically skipped when <Link path="guides/react-website#runtime-configuration">`runtime-config.json`</Link> is not present
|
|
74
|
+
- **Cognito**: Embeds the JWT access token in the `Sec-WebSocket-Protocol` header as a base64url-encoded bearer token, following the [AgentCore WebSocket auth protocol](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-get-started-websocket.html)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
## Infrastructure
|
|
78
|
+
|
|
79
|
+
<Snippet name="connection/react-agent-infrastructure" parentHeading="Infrastructure" />
|
|
80
|
+
|
|
81
|
+
## Using the Generated Code
|
|
82
|
+
|
|
83
|
+
### Using TanStack Query
|
|
84
|
+
|
|
85
|
+
The most common use case is streaming the agent's response using the `invoke` subscription with the `use<AgentName>Agent` hook, which returns a [tRPC options proxy](https://trpc.io/docs/client/tanstack-react-query) for use with TanStack Query:
|
|
86
|
+
|
|
87
|
+
```tsx {1-2,7-22}
|
|
88
|
+
import { useSubscription } from '@trpc/tanstack-react-query';
|
|
89
|
+
import { useMyAgentAgent } from './hooks/useMyAgentAgent';
|
|
90
|
+
|
|
91
|
+
function ChatComponent() {
|
|
92
|
+
const trpc = useMyAgentAgent();
|
|
93
|
+
|
|
94
|
+
const subscription = useSubscription(
|
|
95
|
+
trpc.invoke.subscriptionOptions(
|
|
96
|
+
{ message: 'What can you help me with?' },
|
|
97
|
+
{
|
|
98
|
+
enabled: true,
|
|
99
|
+
onStarted: () => {
|
|
100
|
+
console.log('Agent started responding');
|
|
101
|
+
},
|
|
102
|
+
onData: (token) => {
|
|
103
|
+
console.log('Received token:', token);
|
|
104
|
+
},
|
|
105
|
+
onError: (error) => {
|
|
106
|
+
console.error('Agent error:', error);
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
),
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<div>
|
|
114
|
+
<p>Status: {subscription.status}</p>
|
|
115
|
+
{subscription.data && <p>Latest token: {subscription.data}</p>}
|
|
116
|
+
{subscription.error && <p>Error: {subscription.error.message}</p>}
|
|
117
|
+
</div>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
:::note[tRPC Options Proxy]
|
|
123
|
+
For more details on the tRPC options proxy and TanStack Query integration, see the [tRPC TanStack React Query documentation](https://trpc.io/docs/client/tanstack-react-query).
|
|
124
|
+
:::
|
|
125
|
+
|
|
126
|
+
### Using the Vanilla tRPC Client
|
|
127
|
+
|
|
128
|
+
The `use<AgentName>AgentClient` hook provides access to the [vanilla tRPC client](https://trpc.io/docs/client/vanilla) for more control over the subscription lifecycle:
|
|
129
|
+
|
|
130
|
+
```tsx {2,5,9-22,25}
|
|
131
|
+
import { useState } from 'react';
|
|
132
|
+
import { useMyAgentAgentClient } from './hooks/useMyAgentAgent';
|
|
133
|
+
|
|
134
|
+
function ChatComponent() {
|
|
135
|
+
const client = useMyAgentAgentClient();
|
|
136
|
+
const [messages, setMessages] = useState<string[]>([]);
|
|
137
|
+
|
|
138
|
+
const sendMessage = (message: string) => {
|
|
139
|
+
const subscription = client.invoke.subscribe(
|
|
140
|
+
{ message },
|
|
141
|
+
{
|
|
142
|
+
onData: (token) => {
|
|
143
|
+
setMessages((prev) => [...prev, token]);
|
|
144
|
+
},
|
|
145
|
+
onComplete: () => {
|
|
146
|
+
console.log('Agent finished');
|
|
147
|
+
},
|
|
148
|
+
onError: (error) => {
|
|
149
|
+
console.error('Error:', error);
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
// Clean up when done
|
|
155
|
+
return () => subscription.unsubscribe();
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
return (
|
|
159
|
+
<div>
|
|
160
|
+
<button onClick={() => sendMessage('Hello!')}>Send</button>
|
|
161
|
+
<div>
|
|
162
|
+
{messages.map((msg, i) => (
|
|
163
|
+
<span key={i}>{msg}</span>
|
|
164
|
+
))}
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
:::note[Vanilla tRPC Client]
|
|
172
|
+
For more details on the vanilla tRPC client, see the [tRPC Vanilla Client documentation](https://trpc.io/docs/client/vanilla).
|
|
173
|
+
:::
|
|
174
|
+
|
|
175
|
+
## Local Development
|
|
176
|
+
|
|
177
|
+
The connection generator automatically configures `serve-local` integration for your react website:
|
|
178
|
+
|
|
179
|
+
1. Running `nx serve-local <website>` will also start the agent's local server
|
|
180
|
+
2. The runtime config is overridden to point to the local WebSocket URL (e.g., `ws://localhost:8081/ws`)
|
|
181
|
+
3. Like with connected APIs, authentication is skipped in `serve-local` mode when <Link path="guides/react-website#runtime-configuration">`runtime-config.json`</Link> is not present
|
|
182
|
+
|
|
183
|
+
:::tip[Hot Reloading]
|
|
184
|
+
The website and connected agent will hot-reload, enabling you to quickly iterate on both together without deploying to AWS.
|
|
185
|
+
:::
|
|
186
|
+
|
|
187
|
+
## Type Safety
|
|
188
|
+
|
|
189
|
+
The integration provides complete end-to-end type safety. Your IDE will provide full autocompletion and type checking for all agent procedure calls. The types are automatically inferred from your agent's tRPC router definition, ensuring that any changes to your agent's API are immediately reflected in your frontend code.
|
|
190
|
+
|
|
191
|
+
## More Information
|
|
192
|
+
|
|
193
|
+
For more information, please refer to:
|
|
194
|
+
|
|
195
|
+
- <Link path="guides/ts-agent">TypeScript Agent Guide</Link>
|
|
196
|
+
- [tRPC WebSocket Client](https://trpc.io/docs/client/links/wsLink)
|
|
197
|
+
- [tRPC TanStack React Query Integration](https://trpc.io/docs/client/tanstack-react-query)
|
|
198
|
+
- [Bedrock AgentCore Runtime WebSocket](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-get-started-websocket.html)
|