Saltearse al contenido

Reaccionar a tRPC

El plugin de AWS para Nx proporciona un generador para integrar rápidamente tu API tRPC con un sitio web en React. Configura toda la infraestructura necesaria para conectar con tus backends tRPC, incluyendo soporte para autenticación con AWS IAM y Cognito, además de manejo adecuado de errores. Esta integración ofrece seguridad de tipos de extremo a extremo entre tu frontend y los backends tRPC.

Requisitos previos

Antes de usar este generador, asegúrate que tu aplicación React tenga:

  1. Un archivo main.tsx que renderice tu aplicación
  2. Un elemento JSX <App/> donde se inyectará automáticamente el proveedor tRPC
  3. Un backend tRPC funcional (generado usando el generador de backends tRPC)
  4. Autenticación Cognito añadida mediante el generador ts#cloudscape-website-auth si conectas una API que use autenticación Cognito o IAM
Ejemplo de estructura requerida en 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

  1. Instale el Nx Console VSCode Plugin si aún no lo ha hecho
  2. Abra la consola Nx en VSCode
  3. Haga clic en Generate (UI) en la sección "Common Nx Commands"
  4. Busque @aws/nx-plugin - api-connection
  5. Complete los parámetros requeridos
    • Haga clic en Generate

    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

    Resultado 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 a APIs 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 peticiones HTTP con SigV4 (solo IAM)
        • use<ApiName>.tsx Hook específico para cada API backend. ApiName corresponde al nombre de la API

    Además, instala las dependencias necesarias:

    • @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 provee un hook use<ApiName> que 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>Se produjo un error:</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

    Manejar estados de carga

    Siempre gestiona 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 peticiones 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 al hacer 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

    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 con 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 esquemas de tu backend, garantizando que cualquier cambio en tu API se refleje inmediatamente en el frontend sin necesidad de compilar.

    Más información

    Para más detalles, consulta la documentación de tRPC TanStack React Query.

    También puedes consultar directamente la documentación de TanStack Query.