React a tRPC
Nx Plugin for AWS fornisce un generatore per integrare rapidamente la tua API tRPC con un sito web React. Configura tutte le impostazioni necessarie per connettersi ai tuoi backend tRPC, incluso il supporto per l’autenticazione AWS IAM e Cognito e una corretta gestione degli errori. L’integrazione fornisce una completa type safety end-to-end tra il tuo frontend e i backend tRPC.
Prerequisiti
Sezione intitolata “Prerequisiti”Prima di utilizzare questo generatore, assicurati che la tua applicazione React abbia:
- Un file
main.tsxche renderizza la tua applicazione - Un elemento JSX
<App/>dove il provider tRPC verrà automaticamente iniettato - Un’API tRPC funzionante (generata utilizzando il generatore di API tRPC)
- Cognito Auth aggiunto tramite il generatore
ts#website#authse si connette un’API che utilizza l’autenticazione Cognito o IAM
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
Sezione intitolata “Utilizzo”Esegui il Generatore
Sezione intitolata “Esegui il Generatore”pnpm nx g @aws/nx-plugin:connectionyarn nx g @aws/nx-plugin:connectionnpx nx g @aws/nx-plugin:connectionbunx nx g @aws/nx-plugin:connectionPuoi anche eseguire una prova per vedere quali file verrebbero modificati
pnpm nx g @aws/nx-plugin:connection --dry-runyarn nx g @aws/nx-plugin:connection --dry-runnpx nx g @aws/nx-plugin:connection --dry-runbunx nx g @aws/nx-plugin:connection --dry-run- Installa il Nx Console VSCode Plugin se non l'hai già fatto
- Apri la console Nx in VSCode
- Clicca su
Generate (UI)nella sezione "Common Nx Commands" - Cerca
@aws/nx-plugin - connection - Compila i parametri richiesti
- Clicca su
Generate
Opzioni
Sezione intitolata “Opzioni”| Parametro | Tipo | Predefinito | Descrizione |
|---|---|---|---|
| sourceProject Obbligatorio | string | - | Il progetto sorgente |
| targetProject Obbligatorio | string | - | Il progetto di destinazione a cui connettersi |
| sourceComponent | string | - | Il componente sorgente da cui connettersi (nome del componente, percorso relativo alla radice del progetto sorgente, o id del generatore). Usare '.' per selezionare esplicitamente il progetto come sorgente. |
| targetComponent | string | - | Il componente di destinazione a cui connettersi (nome del componente, percorso relativo alla radice del progetto di destinazione, o id del generatore). Usare '.' per selezionare esplicitamente il progetto come destinazione. |
| preferInstallDependencies | boolean | true | Se preferire l'installazione delle dipendenze dopo l'esecuzione del generatore. Impostare su false per rimandare l'installazione quando si eseguono più generatori in batch (l'installazione viene comunque eseguita se necessaria affinché i generatori successivi possano calcolare il grafo dei progetti Nx); installare una volta alla fine. |
Output del Generatore
Sezione intitolata “Output del Generatore”Il generatore crea la seguente struttura nella tua applicazione React:
Directorysrc
Directorycomponents
- <ApiName>ClientProvider.tsx Configura i client tRPC e i binding agli schemi del tuo backend. ApiName verrà risolto con il nome dell’API
- QueryClientProvider.tsx Provider del client TanStack React Query
Directoryhooks
- useSigV4.tsx Hook per firmare le richieste HTTP con SigV4 (solo IAM)
- use<ApiName>.tsx Un hook che restituisce il proxy delle opzioni tRPC per l’integrazione con TanStack Query
- use<ApiName>Client.tsx Un hook che restituisce il client tRPC vanilla per chiamate API dirette
Inoltre, installa le dipendenze richieste:
@trpc/client@trpc/tanstack-react-query@tanstack/react-queryaws4fetch(se si utilizza l’autenticazione IAM)event-source-polyfill(se si utilizza REST API, per il supporto delle subscription)
Utilizzo del Codice Generato
Sezione intitolata “Utilizzo del Codice Generato”Utilizzo dell’Hook Proxy delle Opzioni tRPC
Sezione intitolata “Utilizzo dell’Hook Proxy delle Opzioni tRPC”Il generatore fornisce un hook use<ApiName> che restituisce un proxy delle opzioni tRPC da utilizzare con gli hook di TanStack Query come useQuery e useMutation:
import { useQuery, useMutation } from '@tanstack/react-query';import { useMyApi } from './hooks/useMyApi';
function MyComponent() { const trpc = useMyApi();
// Example query const { data, isLoading, error } = useQuery(trpc.users.list.queryOptions());
// Example 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> );}Utilizzo del Client tRPC Vanilla
Sezione intitolata “Utilizzo del Client tRPC Vanilla”L’hook use<ApiName>Client fornisce accesso al client tRPC vanilla, che è utile per chiamate API imperative e subscription:
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>;}Gestione degli Errori
Sezione intitolata “Gestione degli Errori”L’integrazione include una gestione degli errori integrata che elabora 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> );}Subscription (Streaming)
Sezione intitolata “Subscription (Streaming)”Quando ci si connette a un backend tRPC REST API, il client generato è automaticamente configurato con un splitLink che instrada le operazioni di subscription attraverso httpSubscriptionLink (utilizzando SSE) e le query/mutation regolari attraverso httpLink. Ciò significa che le subscription funzionano immediatamente senza alcuna configurazione aggiuntiva.
Per informazioni su come definire le procedure di subscription nel tuo backend, consulta la guida al generatore di API tRPC.
Utilizzo dell’Hook useSubscription
Sezione intitolata “Utilizzo dell’Hook useSubscription”Puoi consumare le subscription utilizzando l’hook useSubscription con subscriptionOptions dal proxy delle opzioni:
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> );}L’oggetto subscription fornisce:
subscription.data— i dati ricevuti più di recentesubscription.error— l’errore ricevuto più di recentesubscription.status— uno tra'idle','connecting','pending'o'error'subscription.reset()— reimposta la subscription (utile per recuperare dagli errori)
Utilizzo del Client Vanilla
Sezione intitolata “Utilizzo del Client Vanilla”In alternativa, puoi utilizzare il client tRPC vanilla tramite l’hook use<ApiName>Client per un maggiore controllo sul ciclo di vita della subscription:
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); }, }, );
// Clean up the subscription on unmount return () => subscription.unsubscribe(); }, [client]);
return ( <ul> {messages.map((msg, i) => ( <li key={i}>{msg}</li> ))} </ul> );}Best Practice
Sezione intitolata “Best Practice”Gestire gli Stati di Caricamento
Sezione intitolata “Gestire gli 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
Sezione intitolata “Aggiornamenti Ottimistici”Utilizza gli aggiornamenti ottimistici per una migliore 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) => { // Cancel outgoing fetches await queryClient.cancelQueries(trpc.users.list.queryFilter());
// Get snapshot of current data const previousUsers = queryClient.getQueryData( trpc.users.list.queryKey(), );
// Optimistically remove the user queryClient.setQueryData(trpc.users.list.queryKey(), (old) => old?.filter((user) => user.id !== userId), );
return { previousUsers }; }, onError: (err, userId, context) => { // Restore previous data on error 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)}>Delete</button> </li> ))} </ul> );}Prefetching dei Dati
Sezione intitolata “Prefetching dei Dati”Effettua il prefetch dei dati per migliori prestazioni:
function UserList() { const trpc = useMyApi(); const users = useQuery(trpc.users.list.queryOptions()); const queryClient = useQueryClient();
// Prefetch user details on hover 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
Sezione intitolata “Query Infinite”Gestisci la paginazione con le 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 ? 'Loading...' : 'Load More'} </button> )} </div> );}È importante notare che le query infinite possono essere utilizzate solo per procedure con una proprietà di input denominata cursor.
Type Safety
Sezione intitolata “Type Safety”L’integrazione fornisce una completa type safety end-to-end. Il tuo IDE fornirà il completamento automatico completo e il controllo dei tipi per tutte le tue chiamate API:
function UserForm() { const trpc = useMyApi();
// ✅ Input is fully typed const createUser = trpc.users.create.useMutation();
const handleSubmit = (data: CreateUserInput) => { // ✅ Type error if input doesn't match schema createUser.mutate(data); };
return <form onSubmit={handleSubmit}>{/* ... */}</form>;}I tipi vengono automaticamente inferiti dalle definizioni del router e dello schema del tuo backend, garantendo che qualsiasi modifica alla tua API si rifletta immediatamente nel codice del frontend senza la necessità di effettuare la build.
Autenticazione Personalizzata
Sezione intitolata “Autenticazione Personalizzata”Se la tua API tRPC utilizza l’autenticazione Custom (Lambda Authorizer), il provider del client generato include headers segnaposto dove devi aggiungere le intestazioni di autorizzazione che il tuo authorizer si aspetta. Cerca i commenti // TODO: Add headers required by your custom authorizer nel file generato <ApiName>ClientProvider.tsx e sostituiscili con la logica del tuo token o chiave API.
Ulteriori Informazioni
Sezione intitolata “Ulteriori Informazioni”Per ulteriori informazioni, consulta: