Reagir ao tRPC
O Plugin AWS para Nx fornece um gerador para integrar rapidamente sua API tRPC com um site React. Ele configura toda a infraestrutura necessária para conectar aos seus backends tRPC, incluindo suporte a autenticação AWS IAM e Cognito, além de 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
Seção intitulada “Pré-requisitos”Antes de usar este gerador, certifique-se que sua aplicação React possui:
- Um arquivo
main.tsxque renderiza sua aplicação - Um elemento JSX
<App/>onde o provider tRPC será injetado automaticamente - Uma API tRPC funcional (gerada usando o gerador de API tRPC)
- Cognito Auth adicionado via gerador
ts#react-website-authse conectando a uma API que usa autenticação Cognito ou IAM
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>,);Executar o Gerador
Seção intitulada “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-connectionyarn nx g @aws/nx-plugin:api-connectionnpx nx g @aws/nx-plugin:api-connectionbunx nx g @aws/nx-plugin:api-connectionVocê também pode realizar uma execução simulada para ver quais arquivos seriam alterados
pnpm nx g @aws/nx-plugin:api-connection --dry-runyarn nx g @aws/nx-plugin:api-connection --dry-runnpx nx g @aws/nx-plugin:api-connection --dry-runbunx nx g @aws/nx-plugin:api-connection --dry-run| 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 |
Saída do Gerador
Seção intitulada “Saída do Gerador”O gerador cria a seguinte estrutura em sua aplicação React:
Directorysrc
Directorycomponents
- <ApiName>ClientProvider.tsx Configura os clients tRPC e vinculações aos schemas do backend. ApiName será resolvido para o nome da API
- QueryClientProvider.tsx Provider do client TanStack React Query
Directoryhooks
- useSigV4.tsx Hook para assinar requisições HTTP com SigV4 (apenas IAM)
- use<ApiName>.tsx Hook para a API de backend específica
Além disso, instala as dependências necessárias:
@trpc/client@trpc/tanstack-react-query@tanstack/react-queryaws4fetch(se usando autenticação IAM)
Usando o Código Gerado
Seção intitulada “Usando o Código Gerado”Usando o Hook tRPC
Seção intitulada “Usando o Hook tRPC”O gerador fornece um hook use<ApiName> que dá acesso ao client 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 query const { data, isLoading, error } = useQuery(trpc.users.list.queryOptions());
// Exemplo de 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> );}Tratamento de Erros
Seção intitulada “Tratamento de Erros”A integração inclui tratamento de erros interno 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
Seção intitulada “Melhores Práticas”Lidar com Estados de Carregamento
Seção intitulada “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
Seção intitulada “Atualizações Otimistas”Use atualizações otimistas para melhorar a 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 otimisticamente 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
Seção intitulada “Pré-busca de Dados”Faça pré-busca de 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
Seção intitulada “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
Seção intitulada “Segurança de Tipos”A integração fornece segurança de tipos completa de ponta a ponta. Seu IDE oferecerá autocompletar e verificação de tipos para todas as 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 do router e definições de schema do backend, garantindo que quaisquer mudanças na API sejam imediatamente refletidas no código frontend sem necessidade de build.
Mais Informações
Seção intitulada “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.