Salta ai contenuti

React con tRPC

Il plugin AWS per Nx fornisce un generatore per integrare rapidamente la tua API tRPC con un sito web React. Configura tutte le impostazioni necessarie per connettersi ai backend tRPC, incluso il supporto per l’autenticazione IAM di AWS e una corretta gestione degli errori. L’integrazione garantisce una completa type safety end-to-end tra frontend e backend tRPC.

Prerequisiti

Prima di utilizzare questo generatore, assicurati che la tua applicazione React abbia:

  1. Un file main.tsx che renderizza l’applicazione
  2. Un elemento JSX <App/> dove il provider tRPC verrà iniettato automaticamente
  3. Un backend tRPC funzionante (generato usando il generatore di backend tRPC)
Esempio della struttura richiesta per 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>,
);

Utilizzo

Esegui il Generatore

  1. Installa il Nx Console VSCode Plugin se non l'hai già fatto
  2. Apri la console Nx in VSCode
  3. Clicca su Generate (UI) nella sezione "Common Nx Commands"
  4. Cerca @aws/nx-plugin - api-connection
  5. Compila i parametri richiesti
    • Clicca su Generate

    Opzioni

    Parametro Tipo Predefinito Descrizione
    sourceProject Obbligatorio string - The source project which will call the API
    targetProject Obbligatorio string - The target project containing your API
    auth string IAM Authentication strategy (choose from IAM or None)

    Output del Generatore

    Il generatore crea la seguente struttura nella tua applicazione React:

    • Directorysrc
      • Directorycomponents
        • DirectoryTrpcClients
          • index.tsx
          • TrpcProvider.tsx Provider riutilizzabile per multiple API tRPC
          • TrpcApis.tsx Oggetto contenente tutte le connessioni API tRPC
          • TrpcClientProviders.tsx Configura i client tRPC e i binding agli schemi backend
        • QueryClientProvider.tsx Provider del client TanStack React Query
      • Directoryhooks
        • useSigV4.tsx Hook per firmare richieste HTTP con SigV4 (solo IAM)
        • use<ApiName>.tsx Hook specifico per l’API backend. ApiName corrisponderà al nome dell’API

    Inoltre, installa le dipendenze necessarie:

    • @trpc/client
    • @trpc/tanstack-react-query
    • @tanstack/react-query
    • aws4fetch (se si utilizza l’autenticazione IAM)

    Utilizzo del Codice Generato

    Utilizzo dell’Hook tRPC

    Il generatore fornisce un hook use<ApiName> che dà accesso al client tRPC con type safety:

    import { useQuery, useMutation } from '@tanstack/react-query';
    import { useMyApi } from './hooks/useMyApi';
    function MyComponent() {
    const trpc = useMyApi();
    // Esempio di query
    const { data, isLoading, error } = useQuery(trpc.users.list.queryOptions());
    // Esempio di mutation
    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>
    );
    }

    Gestione degli Errori

    L’integrazione include una gestione degli errori integrata che processa correttamente gli errori tRPC:

    function MyComponent() {
    const trpc = useMyApi();
    const { data, error } = useQuery(trpc.users.list.queryOptions());
    if (error) {
    return (
    <div>
    <h2>Error occurred:</h2>
    <p>{error.message}</p>
    {error.data?.code && <p>Code: {error.data.code}</p>}
    </div>
    );
    }
    return (
    <ul>
    {data.map((user) => (
    <li key={user.id}>{user.name}</li>
    ))}
    </ul>
    );
    }

    Best Practices

    Gestione degli Stati di Caricamento

    Gestisci sempre gli stati di caricamento ed errore per una migliore esperienza utente:

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

    Aggiornamenti Ottimistici

    Utilizza aggiornamenti ottimistici per migliorare l’esperienza utente:

    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) => {
    // Annulla le richieste in corso
    await queryClient.cancelQueries(trpc.users.list.queryFilter());
    // Ottieni snapshot dei dati correnti
    const previousUsers = queryClient.getQueryData(
    trpc.users.list.queryKey(),
    );
    // Rimuovi l'utente in modo ottimistico
    queryClient.setQueryData(trpc.users.list.queryKey(), (old) =>
    old?.filter((user) => user.id !== userId),
    );
    return { previousUsers };
    },
    onError: (err, userId, context) => {
    // Ripristina i dati precedenti in caso di errore
    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)}>Elimina</button>
    </li>
    ))}
    </ul>
    );
    }

    Prefetch dei Dati

    Esegui il prefetch dei dati per migliorare le performance:

    function UserList() {
    const trpc = useMyApi();
    const users = useQuery(trpc.users.list.queryOptions());
    const queryClient = useQueryClient();
    // Prefetch dei dettagli utente al passaggio del mouse
    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>
    );
    }

    Query Infinite

    Gestisci la paginazione con query infinite:

    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 ? 'Caricamento...' : 'Carica Altri'}
    </button>
    )}
    </div>
    );
    }

    È importante notare che le query infinite possono essere utilizzate solo per procedure con una proprietà di input chiamata cursor.

    Type Safety

    L’integrazione fornisce una completa type safety end-to-end. La tua IDE offrirà autocompletamento e type checking per tutte le chiamate API:

    function UserForm() {
    const trpc = useMyApi();
    // ✅ L'input è completamente tipizzato
    const createUser = trpc.users.create.useMutation();
    const handleSubmit = (data: CreateUserInput) => {
    // ✅ Errore di tipo se l'input non corrisponde allo schema
    createUser.mutate(data);
    };
    return <form onSubmit={handleSubmit}>{/* ... */}</form>;
    }

    I tipi vengono inferiti automaticamente dal router e dalle definizioni dello schema del backend, garantendo che qualsiasi modifica all’API si rifletta immediatamente nel codice frontend senza necessità di build.

    Ulteriori Informazioni

    Per maggiori informazioni, consulta la documentazione di tRPC TanStack React Query.

    Puoi anche riferirti direttamente alla documentazione di TanStack Query.