tRPC: APIs End-to-End Type-Safe — Revolucionando a Comunicação entre Front-end e Back-end com TypeScript

O Problema da Duplicação de Tipos
Em aplicacoes web modernas, uma das maiores fontes de bugs e retrabalho e a duplicacao de tipos entre front-end e back-end. Voce define uma interface no servidor, depois a reescreve no cliente, e quando algo muda, os dois lados dessincronizam. O tRPC resolve esse problema de forma elegante: com inferencia automatica de tipos do servidor para o cliente, sem geracao de codigo, sem schemas intermediarios, apenas TypeScript puro.
O que e tRPC?
Criado por Alex / KATT, o tRPC (Transport agnostic RPC) e um framework que permite construir APIs RPC totalmente tipadas entre front-end e back-end usando apenas TypeScript. Diferente de REST ou GraphQL, voce nao define rotas ou schemas separados — voce simplesmente escreve funcoes no servidor e as consome no cliente com autocomplete e verificacao de tipos nativas.
Por que tRPC e Diferente?
- Zero geracao de codigo: ao contrario de GraphQL (codegen) ou REST (contratos manuais), o tRPC infere tipos automaticamente
- TypeScript nativo: aproveita o sistema de tipos do TypeScript ao maximo, sem ferramentas externas
- Transporte agnostico: funciona com HTTP, WebSockets, ou qualquer protocolo
- Seguranca em tempo real: se o servidor mudar um tipo, o cliente quebra na compilacao — nunca em producao
- Serializacao automatica: suporte nativo a Dates, Maps, Sets e tipos complexos via superjson
Arquitetura Basica
O tRPC funciona com tres pecas fundamentais: o router (define os endpoints), o contexto (compartilhado entre todas as chamadas) e o cliente (consome os endpoints com tipos inferidos).
// server/trpc.ts
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
const t = initTRPC.create();
export const appRouter = t.router({
getUser: t.procedure
.input(z.string())
.query(async ({ input }) => {
const user = await db.user.findUnique(input);
return user;
}),
createUser: t.procedure
.input(z.object({
name: z.string(),
email: z.string().email()
}))
.mutation(async ({ input }) => {
return db.user.create({ data: input });
})
});
export type AppRouter = typeof appRouter;Note como cada procedimento ja define validacao com Zod e retorna tipos automaticamente. Nao ha schema GraphQL, nem arquivos .proto, nem geracao de codigo.
Consumindo no Cliente
No front-end, o consumo e igualmente simples e totalmente tipado:
// client/index.ts
import { createTRPCReact } from '@trpc/react-query';
import type { AppRouter } from '../server/trpc';
export const trpc = createTRPCReact<AppRouter>();
// Uso em um componente React
function UserProfile({ userId }: { userId: string }) {
const { data, isLoading } = trpc.getUser.useQuery(userId);
// data e automaticamente tipado como User | undefined
// isLoading e boolean
if (isLoading) return <div>Carregando...</div>;
return <div>{data?.name}</div>;
}O VSCode ja oferece autocomplete para todos os endpoints, parametros e retornos. Se o servidor alterar getUser para retornar um campo novo, o cliente automaticamente o reconhece sem qualquer alteracao manual.
Procedimentos: Query vs Mutation vs Subscription
- Query (.query): para leitura de dados com cache e deduplicacao automatica
- Mutation (.mutation): para escrita de dados com invalidacao de cache pos-operacao
- Subscription (.subscription): para dados em tempo real via WebSockets
Integracao com React Query
O tRPC se integra perfeitamente com @tanstack/react-query, herdando todo o ecossistema de cache, refetch, paginacao e stale-while-revalidate. Basta adicionar o provider:
import { httpBatchLink } from '@trpc/client';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { trpc } from './trpc';
const queryClient = new QueryClient();
const trpcClient = trpc.createClient({
links: [httpBatchLink({ url: 'http://localhost:3000/trpc' })]
});
function App() {
return (
<trpc.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>
<UserProfile userId="42" />
</QueryClientProvider>
</trpc.Provider>
);
}Middlewares e Contexto
O tRPC suporta middlewares encadeaveis para autenticacao, logging, rate limiting e muito mais:
const isAuthed = t.middleware(async ({ ctx, next }) => {
if (!ctx.session?.userId) {
throw new TRPCError({ code: 'UNAUTHORIZED' });
}
return next({
ctx: { userId: ctx.session.userId }
});
});
const protectedProcedure = t.procedure.use(isAuthed);
export const appRouter = t.router({
getPrivateData: protectedProcedure.query(({ ctx }) => {
return db.data.findMany({ where: { userId: ctx.userId } });
})
});tRPC vs REST vs GraphQL
| Caracteristica | REST | GraphQL | tRPC |
|---|---|---|---|
| Seguranca de tipos | Manual | Codegen | Automatica |
| Curva de aprendizado | Baixa | Media | Baixa |
| Cache HTTP nativo | Sim | Nao | Via React Query |
| Batching de requests | Manual | Automatico | Automatico |
| Documentacao gerada | Swagger | GraphiQL | Inferida dos tipos |
| Complexidade de setup | Baixa | Alta | Minima |
Quando Usar tRPC?
O tRPC brilha em aplicacoes full-stack TypeScript onde o front-end e o back-end sao mantidos no mesmo monorepo. E ideal para: aplicacoes Next.js, Remix, ou qualquer setup com Vite + Express; startups que querem prototipar rapidamente sem perder qualidade; times que ja usam TypeScript e querem produtividade maxima. Para cenarios com multiplos consumidores (mobile, web, terceiros), GraphQL ou REST ainda podem ser mais adequados.
Conclusao
O tRPC representa uma mudanca de paradigma na comunicacao entre front-end e back-end. Ao eliminar a duplicacao de tipos e a geracao de codigo, ele permite que desenvolvedores se concentrem no que realmente importa: a logica de negocios. Se voce ja usa TypeScript no full-stack, experimentar o tRPC e um dos upgrades de produtividade mais impactantes que voce pode fazer no seu projeto em 2026.







