Reagir ao tRPC
O AWS Plugin para Nx fornece um gerador para integrar rapidamente sua API tRPC com um site React. Ele configura toda a infraestrutura necessária para conexão com seus backends tRPC, incluindo suporte a autenticação IAM da AWS e tratamento adequado de erros. A integração oferece segurança de tipos completa de ponta a ponta entre seu frontend e backend(s) tRPC.
Pré-requisitos
Antes de usar este gerador, certifique-se que sua aplicação React possui:
- Um arquivo
main.tsx
que renderiza sua aplicação - Um elemento JSX
<App/>
onde o provider tRPC será injetado automaticamente - Um backend tRPC funcional (gerado usando o gerador de backend tRPC)
Exemplo da estrutura necessária do 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>,);
Utilização
Executar o Gerador
- Instale o Nx Console VSCode Plugin se ainda não o fez
- Abra o console Nx no VSCode
- Clique em
Generate (UI)
na seção "Common Nx Commands" - Procure por
@aws/nx-plugin - api-connection
- Preencha os parâmetros obrigatórios
- Clique em
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
Você também pode realizar uma execução simulada para ver quais arquivos seriam alterados
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
Opções
Parâmetro | Tipo | Padrão | Descrição |
---|---|---|---|
sourceProject Obrigatório | string | - | The source project which will call the API |
targetProject Obrigatório | string | - | The target project containing your API |
auth | string | IAM | Authentication strategy (choose from IAM or None) |
Saída do Gerador
O gerador cria a seguinte estrutura em sua aplicação React:
Directorysrc
Directorycomponents
DirectoryTrpcClients
- index.tsx
- TrpcProvider.tsx Provider reutilizável para múltiplas APIs tRPC
- TrpcApis.tsx Objeto contendo todas as conexões de API tRPC
- TrpcClientProviders.tsx Configura os clientes tRPC e vinculações aos schemas do backend
- QueryClientProvider.tsx Provider do cliente TanStack React Query
Directoryhooks
- useSigV4.tsx Hook para assinar requisições HTTP com SigV4 (apenas IAM)
- use<ApiName>.tsx Hook específico para a API do backend. ApiName será resolvido para o nome da API
Adicionalmente, instala as dependências necessárias:
@trpc/client
@trpc/tanstack-react-query
@tanstack/react-query
aws4fetch
(se usando autenticação IAM)
Usando o Código Gerado
Usando o Hook tRPC
O gerador fornece um hook use<ApiName>
que dá acesso ao cliente tRPC com segurança de tipos:
import { useQuery, useMutation } from '@tanstack/react-query';import { useMyApi } from './hooks/useMyApi';
function MyComponent() { const trpc = useMyApi();
// Exemplo de consulta const { data, isLoading, error } = useQuery(trpc.users.list.queryOptions());
// Exemplo de mutação const mutation = useMutation(trpc.users.create.mutationOptions());
const handleCreate = () => { mutation.mutate({ name: 'John Doe', email: 'john@example.com', }); };
if (isLoading) return <div>Carregando...</div>;
return ( <ul> {data.map((user) => ( <li key={user.id}>{user.name}</li> ))} </ul> );}
Tratamento de Erros
A integração inclui tratamento de erros integrado que processa adequadamente erros tRPC:
function MyComponent() { const trpc = useMyApi();
const { data, error } = useQuery(trpc.users.list.queryOptions());
if (error) { return ( <div> <h2>Ocorreu um erro:</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> );}
Melhores Práticas
Lidar com Estados de Carregamento
Sempre trate estados de carregamento e erro para melhor experiência do usuário:
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> );}
Atualizações Otimistas
Use atualizações otimistas para melhor experiência do usuário:
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) => { // Cancela requisições pendentes await queryClient.cancelQueries(trpc.users.list.queryFilter());
// Obtém snapshot dos dados atuais const previousUsers = queryClient.getQueryData( trpc.users.list.queryKey(), );
// Remove otimistamente o usuário queryClient.setQueryData(trpc.users.list.queryKey(), (old) => old?.filter((user) => user.id !== userId), );
return { previousUsers }; }, onError: (err, userId, context) => { // Restaura dados anteriores em caso de erro 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)}>Excluir</button> </li> ))} </ul> );}
Pré-busca de Dados
Pré-busque dados para melhor performance:
function UserList() { const trpc = useMyApi(); const users = useQuery(trpc.users.list.queryOptions()); const queryClient = useQueryClient();
// Pré-busca detalhes do usuário ao passar o 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
Gerencie paginação com 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 ? 'Carregando...' : 'Carregar Mais'} </button> )} </div> );}
É importante notar que consultas infinitas só podem ser usadas para procedimentos com uma propriedade de input chamada cursor
.
Segurança de Tipos
A integração fornece segurança de tipos completa de ponta a ponta. Sua IDE oferecerá autocompletar e verificação de tipos para todas chamadas de API:
function UserForm() { const trpc = useMyApi();
// ✅ Input totalmente tipado const createUser = trpc.users.create.useMutation();
const handleSubmit = (data: CreateUserInput) => { // ✅ Erro de tipo se input não corresponder ao schema createUser.mutate(data); };
return <form onSubmit={handleSubmit}>{/* ... */}</form>;}
Os tipos são inferidos automaticamente das definições do router e schema do seu backend, garantindo que quaisquer mudanças na API sejam imediatamente refletidas no código frontend sem necessidade de build.
Mais Informações
Para mais informações, consulte a documentação tRPC TanStack React Query.
Você também pode consultar diretamente a documentação do TanStack Query.