Skip to content

ReactからtRPCへ

Nx向けAWSプラグインは、tRPC APIとReactウェブサイトを迅速に統合するためのジェネレータを提供します。AWS IAMおよびCognito認証サポート、適切なエラーハンドリングを含む、tRPCバックエンド接続に必要なすべての設定を構成します。この統合により、フロントエンドとtRPCバックエンド間の完全なエンドツーエンド型安全性が保証されます。

前提条件

このジェネレータを使用する前に、Reactアプリケーションが以下を満たしていることを確認してください:

  1. アプリケーションをレンダリングするmain.tsxファイルが存在すること
  2. tRPCプロバイダが自動注入される<App/> JSX要素が含まれていること
  3. tRPCバックエンドジェネレータで生成された動作可能なtRPCバックエンドが存在すること
  4. CognitoまたはIAM認証を使用するAPIに接続する場合、ts#cloudscape-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>,
);

使用方法

ジェネレータの実行

  1. インストール Nx Console VSCode Plugin まだインストールしていない場合
  2. VSCodeでNxコンソールを開く
  3. クリック Generate (UI) "Common Nx Commands"セクションで
  4. 検索 @aws/nx-plugin - api-connection
  5. 必須パラメータを入力
    • クリック Generate

    オプション

    パラメータ デフォルト 説明
    sourceProject 必須 string - The source project which will call the API
    targetProject 必須 string - The target project containing your API

    ジェネレータの出力

    ジェネレータはReactアプリケーション内に以下の構造を作成します:

    • Directorysrc
      • Directorycomponents
        • DirectoryTrpcClients
          • index.tsx
          • TrpcProvider.tsx 複数のtRPC APIで再利用されるプロバイダ
          • TrpcApis.tsx すべてのtRPC API接続を含むオブジェクト
          • TrpcClientProviders.tsx tRPCクライアントとバックエンドスキーマのバインディングを設定
        • QueryClientProvider.tsx TanStack React Queryクライアントプロバイダ
      • Directoryhooks
        • useSigV4.tsx SigV4によるHTTPリクエスト署名用フック(IAM専用)
        • use<ApiName>.tsx 特定のバックエンドAPI用フック。ApiNameはAPI名に解決

    さらに、以下の依存関係をインストールします:

    • @trpc/client
    • @trpc/tanstack-react-query
    • @tanstack/react-query
    • aws4fetch(IAM認証使用時)

    生成コードの使用方法

    tRPCフックの使用

    ジェネレータが提供するuse<ApiName>フックを使用して、型安全な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エラーを適切に処理する組み込みのエラーハンドリングが含まれます:

    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>
    );
    }

    ベストプラクティス

    ローディング状態の処理

    ユーザーエクスペリエンス向上のため、常にローディング状態とエラー状態を処理してください:

    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>
    );
    }

    データのプリフェッチ

    パフォーマンス向上のためデータをプリフェッチ:

    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に変更があった場合でもフロントエンドコードをビルドせずに即座に反映されます。

    詳細情報

    詳細については、tRPC TanStack React Query ドキュメントを参照してください。

    直接TanStack Query ドキュメントを参照することもできます。