React a tRPC
Nx Plugin for AWS proporciona un generador para integrar rápidamente tu API tRPC con un sitio web React. Configura toda la configuración necesaria para conectarse a tus backends tRPC, incluyendo soporte para autenticación AWS IAM y Cognito y manejo adecuado de errores. La integración proporciona seguridad de tipos de extremo a extremo completa entre tu frontend y backend(s) tRPC.
Requisitos previos
Sección titulada «Requisitos previos»Antes de usar este generador, asegúrate de que tu aplicación React tenga:
- Un archivo
main.tsxque renderice tu aplicación - Un elemento JSX
<App/>donde el proveedor tRPC se inyectará automáticamente - Una API tRPC funcional (generada usando el generador de API tRPC)
- Cognito Auth agregado a través del generador
ts#website#authsi conectas una API que usa autenticación Cognito o IAM
Ejemplo de estructura requerida de 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>,);Ejecutar el generador
Sección titulada «Ejecutar el generador»pnpm nx g @aws/nx-plugin:connectionyarn nx g @aws/nx-plugin:connectionnpx nx g @aws/nx-plugin:connectionbunx nx g @aws/nx-plugin:connectionTambién puede realizar una ejecución en seco para ver qué archivos se cambiarían
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- Instale el Nx Console VSCode Plugin si aún no lo ha hecho
- Abra la consola Nx en VSCode
- Haga clic en
Generate (UI)en la sección "Common Nx Commands" - Busque
@aws/nx-plugin - connection - Complete los parámetros requeridos
- Haga clic en
Generate
Opciones
Sección titulada «Opciones»| Parámetro | Tipo | Predeterminado | Descripción |
|---|---|---|---|
| sourceProject Requerido | string | - | El proyecto de origen |
| targetProject Requerido | string | - | El proyecto de destino al que conectar |
| sourceComponent | string | - | El componente de origen desde el que conectar (nombre del componente, ruta relativa a la raíz del proyecto de origen, o id del generador). Use '.' para seleccionar explícitamente el proyecto como origen. |
| targetComponent | string | - | El componente de destino al que conectar (nombre del componente, ruta relativa a la raíz del proyecto de destino, o id del generador). Use '.' para seleccionar explícitamente el proyecto como destino. |
| preferInstallDependencies | boolean | true | Si se prefiere instalar las dependencias después de que se ejecute el generador. Establecer en false para diferir la instalación al ejecutar múltiples generadores en lote (la instalación aún se ejecuta si es necesario para que los generadores subsiguientes puedan calcular el grafo de proyectos de Nx); instalar una vez al final. |
Salida del generador
Sección titulada «Salida del generador»El generador crea la siguiente estructura en tu aplicación React:
Directoriosrc
Directoriocomponents
- <ApiName>ClientProvider.tsx Configura los clientes tRPC y enlaces a tu(s) esquema(s) de backend. ApiName se resolverá al nombre de la API
- QueryClientProvider.tsx Proveedor del cliente TanStack React Query
Directoriohooks
- useSigV4.tsx Hook para firmar solicitudes HTTP con SigV4 (solo IAM)
- use<ApiName>.tsx Un hook que devuelve el proxy de opciones tRPC para integración con TanStack Query
- use<ApiName>Client.tsx Un hook que devuelve el cliente tRPC vanilla para llamadas directas a la API
Además, instala las dependencias requeridas:
@trpc/client@trpc/tanstack-react-query@tanstack/react-queryaws4fetch(si se usa autenticación IAM)event-source-polyfill(si se usa REST API, para soporte de suscripciones)
Usar el código generado
Sección titulada «Usar el código generado»Usar el hook de proxy de opciones tRPC
Sección titulada «Usar el hook de proxy de opciones tRPC»El generador proporciona un hook use<ApiName> que devuelve un proxy de opciones tRPC para usar con hooks de TanStack Query como useQuery y 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> );}Usar el cliente tRPC vanilla
Sección titulada «Usar el cliente tRPC vanilla»El hook use<ApiName>Client proporciona acceso al cliente tRPC vanilla, que es útil para llamadas imperativas a la API y suscripciones:
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>;}Manejo de errores
Sección titulada «Manejo de errores»La integración incluye manejo de errores incorporado que procesa adecuadamente los errores de 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> );}Suscripciones (Streaming)
Sección titulada «Suscripciones (Streaming)»Al conectarse a un backend tRPC de REST API, el cliente generado se configura automáticamente con un splitLink que enruta las operaciones de suscripción a través de httpSubscriptionLink (usando SSE) y las consultas/mutaciones regulares a través de httpLink. Esto significa que las suscripciones funcionan de inmediato sin configuración adicional.
Para obtener información sobre cómo definir procedimientos de suscripción en tu backend, consulta la guía del generador de API tRPC.
Usar el hook useSubscription
Sección titulada «Usar el hook useSubscription»Puedes consumir suscripciones usando el hook useSubscription con subscriptionOptions del proxy de opciones:
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> );}El objeto subscription proporciona:
subscription.data— los datos recibidos más recientementesubscription.error— el error recibido más recientementesubscription.status— uno de'idle','connecting','pending', o'error'subscription.reset()— reinicia la suscripción (útil para recuperarse de errores)
Usar el cliente vanilla
Sección titulada «Usar el cliente vanilla»Alternativamente, puedes usar el cliente tRPC vanilla a través del hook use<ApiName>Client para tener más control sobre el ciclo de vida de la suscripción:
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> );}Mejores prácticas
Sección titulada «Mejores prácticas»Manejar estados de carga
Sección titulada «Manejar estados de carga»Siempre maneja los estados de carga y error para una mejor experiencia de usuario:
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> );}Actualizaciones optimistas
Sección titulada «Actualizaciones optimistas»Usa actualizaciones optimistas para una mejor experiencia de usuario:
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> );}Precarga de datos
Sección titulada «Precarga de datos»Precarga datos para un mejor rendimiento:
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> );}Consultas infinitas
Sección titulada «Consultas infinitas»Maneja la paginación con consultas infinitas:
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> );}Es importante tener en cuenta que las consultas infinitas solo se pueden usar para procedimientos con una propiedad de entrada llamada cursor.
Seguridad de tipos
Sección titulada «Seguridad de tipos»La integración proporciona seguridad de tipos de extremo a extremo completa. Tu IDE proporcionará autocompletado completo y verificación de tipos para todas tus llamadas a la 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>;}Los tipos se infieren automáticamente de las definiciones del router y esquema de tu backend, asegurando que cualquier cambio en tu API se refleje inmediatamente en tu código frontend sin necesidad de compilar.
Autenticación personalizada
Sección titulada «Autenticación personalizada»Si tu API tRPC usa autenticación Custom (Lambda Authorizer), el proveedor de cliente generado incluye headers de marcador de posición donde debes agregar los encabezados de autorización que tu autorizador espera. Busca los comentarios // TODO: Add headers required by your custom authorizer en el archivo <ApiName>ClientProvider.tsx generado y reemplázalos con tu lógica de token o clave de API.
Más información
Sección titulada «Más información»Para obtener más información, consulta: