ReactからtRPC
Nx向けAWSプラグインは、tRPC APIをReactウェブサイトと迅速に統合するためのジェネレータを提供します。AWS IAMやCognito認証のサポート、適切なエラーハンドリングを含む、tRPCバックエンド接続に必要なすべての設定を構成します。この統合により、フロントエンドとtRPCバックエンド間の完全なエンドツーエンド型安全性が保証されます。
このジェネレータを使用する前に、Reactアプリケーションが以下を満たしていることを確認してください:
- アプリケーションをレンダリングする
main.tsxファイルが存在すること - tRPCプロバイダが自動注入される
<App/>JSX要素が含まれていること - 動作するtRPC APIが存在すること(tRPC APIジェネレータで生成されたもの)
- CognitoまたはIAM認証を使用するAPIに接続する場合、
ts#react-website-authジェネレータでCognito Authが追加されていること
必要なmain.tsx構造の例
import { StrictMode } from 'react';import * as ReactDOM from 'react-dom/client';import App from './app/app';
const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement,);root.render( <StrictMode> <App /> </StrictMode>,);ジェネレータの実行
Section titled “ジェネレータの実行”- インストール Nx Console VSCode Plugin まだインストールしていない場合
- VSCodeでNxコンソールを開く
- クリック
Generate (UI)"Common Nx Commands"セクションで - 検索
@aws/nx-plugin - connection - 必須パラメータを入力
- クリック
Generate
pnpm nx g @aws/nx-plugin:connectionyarn nx g @aws/nx-plugin:connectionnpx nx g @aws/nx-plugin:connectionbunx nx g @aws/nx-plugin:connection| パラメータ | 型 | デフォルト | 説明 |
|---|---|---|---|
| sourceProject 必須 | string | - | ソース プロジェクト |
| targetProject 必須 | string | - | 接続先のターゲット プロジェクト |
| sourceComponent | string | - | 接続元のソース コンポーネント (コンポーネント名、ソース プロジェクト ルートからの相対パス、またはジェネレーター ID)。プロジェクトをソースとして明示的に選択するには '.' を使用します。 |
| targetComponent | string | - | 接続先のターゲット コンポーネント (コンポーネント名、ターゲット プロジェクト ルートからの相対パス、またはジェネレーター ID)。プロジェクトをターゲットとして明示的に選択するには '.' を使用します。 |
ジェネレータの出力
Section titled “ジェネレータの出力”ジェネレータはReactアプリケーションに以下の構造を作成します:
Directorysrc
Directorycomponents
- <ApiName>ClientProvider.tsx tRPCクライアントとバックエンドスキーマへのバインディングを設定。ApiNameはAPI名に解決されます
- QueryClientProvider.tsx TanStack React Queryクライアントプロバイダ
Directoryhooks
- useSigV4.tsx SigV4によるHTTPリクエスト署名用フック(IAM専用)
- use<ApiName>.tsx TanStack Query統合用のtRPCオプションプロキシを返すフック
- use<ApiName>Client.tsx 直接API呼び出し用のバニラtRPCクライアントを返すフック
追加で以下の依存関係をインストールします:
@trpc/client@trpc/tanstack-react-query@tanstack/react-queryaws4fetch(IAM認証使用時)event-source-polyfill(REST API使用時、サブスクリプションサポート用)
生成コードの使用方法
Section titled “生成コードの使用方法”tRPCオプションプロキシフックの使用
Section titled “tRPCオプションプロキシフックの使用”ジェネレータが提供するuse<ApiName>フックは、useQueryやuseMutationなどのTanStack Queryフックで使用するtRPCオプションプロキシを返します:
import { useQuery, useMutation } from '@tanstack/react-query';import { useMyApi } from './hooks/useMyApi';
function MyComponent() { const trpc = useMyApi();
// クエリの例 const { data, isLoading, error } = useQuery(trpc.users.list.queryOptions());
// ミューテーションの例 const mutation = useMutation(trpc.users.create.mutationOptions());
const handleCreate = () => { mutation.mutate({ name: 'John Doe', email: 'john@example.com', }); };
if (isLoading) return <div>Loading...</div>;
return ( <ul> {data.map((user) => ( <li key={user.id}>{user.name}</li> ))} </ul> );}バニラtRPCクライアントの使用
Section titled “バニラtRPCクライアントの使用”use<ApiName>ClientフックはバニラtRPCクライアントへのアクセスを提供し、命令的なAPI呼び出しやサブスクリプションに便利です:
import { useState } from 'react';import { useMyApiClient } from './hooks/useMyApi';
function MyComponent() { const client = useMyApiClient();
const handleClick = async () => { const result = await client.echo.query({ message: 'Hello!' }); console.log(result);
const mutationResult = await client.users.create.mutate({ name: 'Jane' }); console.log(mutationResult); };
return <button onClick={handleClick}>Call API</button>;}エラーハンドリング
Section titled “エラーハンドリング”統合されたエラーハンドリングによりtRPCエラーを適切に処理:
function MyComponent() { const trpc = useMyApi();
const { data, error } = useQuery(trpc.users.list.queryOptions());
if (error) { return ( <div> <h2>エラーが発生しました:</h2> <p>{error.message}</p> {error.data?.code && <p>コード: {error.data.code}</p>} </div> ); }
return ( <ul> {data.map((user) => ( <li key={user.id}>{user.name}</li> ))} </ul> );}サブスクリプション(ストリーミング)
Section titled “サブスクリプション(ストリーミング)”REST API tRPCバックエンドに接続する場合、生成されたクライアントは自動的にsplitLinkで構成され、サブスクリプション操作はhttpSubscriptionLink(SSEを使用)を経由し、通常のクエリ/ミューテーションはhttpLinkを経由します。つまり、サブスクリプションは追加の設定なしですぐに動作します。
バックエンドでサブスクリプションプロシージャを定義する方法については、ts#trpc-apiジェネレータガイドを参照してください。
useSubscriptionフックの使用
Section titled “useSubscriptionフックの使用”オプションプロキシのsubscriptionOptionsを使用してuseSubscriptionフックでサブスクリプションを利用できます:
import { useSubscription } from '@trpc/tanstack-react-query';import { useMyApi } from './hooks/useMyApi';
function StreamingComponent() { const trpc = useMyApi();
const subscription = useSubscription( trpc.myStream.subscriptionOptions( { query: 'hello' }, { enabled: true, onStarted: () => { console.log('Subscription started'); }, onData: (data) => { console.log('Received:', data.text); }, onError: (error) => { console.error('Subscription error:', error); }, }, ), );
return ( <div> <p>Status: {subscription.status}</p> {subscription.data && <p>Latest: {subscription.data.text}</p>} {subscription.error && <p>Error: {subscription.error.message}</p>} <button onClick={() => subscription.reset()}>Reset</button> </div> );}subscriptionオブジェクトは以下を提供します:
subscription.data— 最後に受信したデータsubscription.error— 最後に受信したエラーsubscription.status—'idle'、'connecting'、'pending'、または'error'のいずれかsubscription.reset()— サブスクリプションをリセット(エラーからの復旧に便利)
バニラクライアントの使用
Section titled “バニラクライアントの使用”または、use<ApiName>Clientフック経由でバニラtRPCクライアントを使用して、サブスクリプションのライフサイクルをより細かく制御できます:
import { useState, useEffect } from 'react';import { useMyApiClient } from './hooks/useMyApi';
function StreamingComponent() { const client = useMyApiClient(); const [messages, setMessages] = useState<string[]>([]);
useEffect(() => { const subscription = client.myStream.subscribe( { query: 'hello' }, { onData: (data) => { setMessages((prev) => [...prev, data.text]); }, onComplete: () => { console.log('Stream complete'); }, onError: (error) => { console.error('Stream error:', error); }, }, );
// アンマウント時にサブスクリプションをクリーンアップ return () => subscription.unsubscribe(); }, [client]);
return ( <ul> {messages.map((msg, i) => ( <li key={i}>{msg}</li> ))} </ul> );}ベストプラクティス
Section titled “ベストプラクティス”ローディング状態の処理
Section titled “ローディング状態の処理”ユーザーエクスペリエンス向上のため、ローディングとエラー状態を常に処理:
function UserList() { const trpc = useMyApi();
const users = useQuery(trpc.users.list.queryOptions());
if (users.isLoading) { return <LoadingSpinner />; }
if (users.error) { return <ErrorMessage error={users.error} />; }
return ( <ul> {users.data.map((user) => ( <li key={user.id}>{user.name}</li> ))} </ul> );}ユーザーエクスペリエンス向上のため楽観的更新を実装:
import { useQueryClient, useQuery, useMutation } from '@tanstack/react-query';
function UserList() { const trpc = useMyApi(); const users = useQuery(trpc.users.list.queryOptions()); const queryClient = useQueryClient();
const deleteMutation = useMutation( trpc.users.delete.mutationOptions({ onMutate: async (userId) => { // 進行中のフェッチをキャンセル await queryClient.cancelQueries(trpc.users.list.queryFilter());
// 現在のデータのスナップショット取得 const previousUsers = queryClient.getQueryData( trpc.users.list.queryKey(), );
// 楽観的にユーザーを削除 queryClient.setQueryData(trpc.users.list.queryKey(), (old) => old?.filter((user) => user.id !== userId), );
return { previousUsers }; }, onError: (err, userId, context) => { // エラー時に以前のデータを復元 queryClient.setQueryData( trpc.users.list.queryKey(), context?.previousUsers, ); }, }), );
return ( <ul> {users.map((user) => ( <li key={user.id}> {user.name} <button onClick={() => deleteMutation.mutate(user.id)}>削除</button> </li> ))} </ul> );}データのプリフェッチ
Section titled “データのプリフェッチ”パフォーマンス向上のためデータをプリフェッチ:
function UserList() { const trpc = useMyApi(); const users = useQuery(trpc.users.list.queryOptions()); const queryClient = useQueryClient();
// ホバー時にユーザー詳細をプリフェッチ const prefetchUser = async (userId: string) => { await queryClient.prefetchQuery(trpc.users.getById.queryOptions(userId)); };
return ( <ul> {users.map((user) => ( <li key={user.id} onMouseEnter={() => prefetchUser(user.id)}> <Link to={`/users/${user.id}`}>{user.name}</Link> </li> ))} </ul> );}ページネーションを無限クエリで処理:
function UserList() { const trpc = useMyApi();
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useInfiniteQuery( trpc.users.list.infiniteQueryOptions( { limit: 10 }, { getNextPageParam: (lastPage) => lastPage.nextCursor, }, ), );
return ( <div> {data?.pages.map((page) => page.users.map((user) => <UserCard key={user.id} user={user} />), )}
{hasNextPage && ( <button onClick={() => fetchNextPage()} disabled={isFetchingNextPage}> {isFetchingNextPage ? '読み込み中...' : 'さらに読み込む'} </button> )} </div> );}無限クエリはcursorという名前の入力プロパティを持つプロシージャでのみ使用可能です。
この統合は完全なエンドツーエンド型安全性を提供します。IDEはすべてのAPI呼び出しに対して完全なオートコンプリートと型チェックをサポート:
function UserForm() { const trpc = useMyApi();
// ✅ 入力は完全に型付けされる const createUser = trpc.users.create.useMutation();
const handleSubmit = (data: CreateUserInput) => { // ✅ スキーマと不一致の場合型エラー createUser.mutate(data); };
return <form onSubmit={handleSubmit}>{/* ... */}</form>;}型はバックエンドのルーターとスキーマ定義から自動推論されるため、APIに変更があってもフロントエンドコードをビルドせずに即時反映されます。
詳細については以下を参照してください:
Custom Auth
Section titled “Custom Auth”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.