Reaccionar a tRPC
El Plugin AWS para Nx proporciona un generador para integrar rápidamente tu API tRPC con un sitio web React. Configura toda la infraestructura necesaria para conectar con tus backends tRPC, incluyendo soporte para autenticación IAM de AWS y manejo adecuado de errores. La integración ofrece seguridad de tipos completa de extremo a extremo entre tu frontend y backend(s) tRPC.
Requisitos previos
Antes de usar este generador, asegúrate que tu aplicación React tiene:
- Un archivo
main.tsx
que renderiza tu aplicación - Un elemento JSX
<App/>
donde se inyectará automáticamente el proveedor tRPC - Un backend tRPC funcional (generado usando el generador de backend tRPC)
Ejemplo de estructura requerida para 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>,);
Uso
Ejecutar el generador
- 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 - api-connection
- Complete los parámetros requeridos
- Haga clic en
Generate
pnpm nx g @aws/nx-plugin:api-connection
yarn nx g @aws/nx-plugin:api-connection
npx nx g @aws/nx-plugin:api-connection
bunx nx g @aws/nx-plugin:api-connection
También puede realizar una ejecución en seco para ver qué archivos se cambiarían
pnpm nx g @aws/nx-plugin:api-connection --dry-run
yarn nx g @aws/nx-plugin:api-connection --dry-run
npx nx g @aws/nx-plugin:api-connection --dry-run
bunx nx g @aws/nx-plugin:api-connection --dry-run
Opciones
Parámetro | Tipo | Predeterminado | Descripción |
---|---|---|---|
sourceProject Requerido | string | - | The source project which will call the API |
targetProject Requerido | string | - | The target project containing your API |
auth | string | IAM | Authentication strategy (choose from IAM or None) |
Salida del generador
El generador crea la siguiente estructura en tu aplicación React:
Directorysrc
Directorycomponents
DirectoryTrpcClients
- index.tsx
- TrpcProvider.tsx Proveedor reutilizable para múltiples APIs tRPC
- TrpcApis.tsx Objeto con todas tus conexiones API tRPC
- TrpcClientProviders.tsx Configura los clientes tRPC y su vinculación con los esquemas del backend
- QueryClientProvider.tsx Proveedor del cliente TanStack React Query
Directoryhooks
- useSigV4.tsx Hook para firmar solicitudes HTTP con SigV4 (solo IAM)
- use<ApiName>.tsx Hook para la API backend específica. ApiName corresponderá al nombre de la API
Adicionalmente, instala las dependencias requeridas:
@trpc/client
@trpc/tanstack-react-query
@tanstack/react-query
aws4fetch
(si se usa autenticación IAM)
Usando el código generado
Usando el Hook tRPC
El generador proporciona un hook use<ApiName>
que te da acceso al cliente tRPC con seguridad de tipos:
import { useQuery, useMutation } from '@tanstack/react-query';import { useMyApi } from './hooks/useMyApi';
function MyComponent() { const trpc = useMyApi();
// Ejemplo de consulta const { data, isLoading, error } = useQuery(trpc.users.list.queryOptions());
// Ejemplo de mutación 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> );}
Manejo de errores
La integración incluye manejo de errores integrado que procesa correctamente los errores tRPC:
function MyComponent() { const trpc = useMyApi();
const { data, error } = useQuery(trpc.users.list.queryOptions());
if (error) { return ( <div> <h2>Error ocurrido:</h2> <p>{error.message}</p> {error.data?.code && <p>Código: {error.data.code}</p>} </div> ); }
return ( <ul> {data.map((user) => ( <li key={user.id}>{user.name}</li> ))} </ul> );}
Mejores prácticas
Manejo de 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
Usa actualizaciones optimistas para mejorar la 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) => { // Cancelar solicitudes pendientes await queryClient.cancelQueries(trpc.users.list.queryFilter());
// Obtener snapshot de datos actuales const previousUsers = queryClient.getQueryData( trpc.users.list.queryKey(), );
// Eliminar usuario optimistamente queryClient.setQueryData(trpc.users.list.queryKey(), (old) => old?.filter((user) => user.id !== userId), );
return { previousUsers }; }, onError: (err, userId, context) => { // Restaurar datos anteriores en caso de 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)}>Eliminar</button> </li> ))} </ul> );}
Precarga de datos
Precarga datos para mejor rendimiento:
function UserList() { const trpc = useMyApi(); const users = useQuery(trpc.users.list.queryOptions()); const queryClient = useQueryClient();
// Precargar detalles de usuario al pasar el 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> );}
Consultas infinitas
Maneja 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 ? 'Cargando...' : 'Cargar más'} </button> )} </div> );}
Es importante notar que las consultas infinitas solo pueden usarse para procedimientos que tengan una propiedad de entrada llamada cursor
.
Seguridad de tipos
La integración provee seguridad de tipos completa de extremo a extremo. Tu IDE ofrecerá autocompletado y verificación de tipos para todas las llamadas API:
function UserForm() { const trpc = useMyApi();
// ✅ La entrada está totalmente tipada const createUser = trpc.users.create.useMutation();
const handleSubmit = (data: CreateUserInput) => { // ✅ Error de tipo si la entrada no coincide con el esquema 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.
Más información
Para más información, consulta la documentación de tRPC TanStack React Query.
También puedes consultar directamente la documentación de TanStack Query.