tRPC Review 2026: APIs Without the API Layer
tRPC lets your frontend call backend functions with full type safety. No REST endpoints, no GraphQL schema, no codegen. Change the server, types update on the client instantly. After using it for two full-stack apps, here's the review.
What I Like
Zero API Layer
// Server: define a procedure
const appRouter = router({
user: router({
get: publicProcedure
.input(z.object({ id: z.string() }))
.query(async ({ input }) => {
return db.query.users.findFirst({ where: eq(users.id, input.id) })
}),
update: protectedProcedure
.input(z.object({ id: z.string(), name: z.string() }))
.mutation(async ({ input }) => {
return db.update(users).set({ name: input.name }).where(eq(users.id, input.id))
}),
}),
})
// Client: call it like a function
const user = trpc.user.get.useQuery({ id: '123' })
// user.data is fully typed!
No fetch calls. No URL strings. No response parsing. Just function calls with types.
Instant Type Updates
Change the server return type → TypeScript immediately errors on the client if you're using a field that no longer exists. No codegen step. No stale types.
Zod Validation
Input validation with Zod is built-in:
.input(z.object({
email: z.string().email(),
age: z.number().min(18),
}))
Invalid input is rejected before your function runs. Validation errors are typed on the client.
React Query Integration
tRPC wraps React Query (TanStack Query). You get caching, invalidation, optimistic updates, and loading states for free.
Subscriptions
const subscription = trpc.chat.onMessage.useSubscription(undefined, {
onData: (message) => setMessages(prev => [...prev, message]),
})
Real-time subscriptions with the same type safety.
What I Don't Like
TypeScript Only
tRPC only works when both client and server are TypeScript. No mobile (Swift/Kotlin), no Python, no external consumers. For public APIs, you still need REST or GraphQL.
Monorepo Preferred
Works best when client and server share a TypeScript project or monorepo. Separate repos require exporting the router type, which adds friction.
Learning Curve
Understanding routers, procedures, contexts, and middleware takes time. The mental model is different from REST.
Debugging
Network requests in DevTools show as POST to /api/trpc/user.get with a JSON body. Less readable than REST's GET /api/users/123. Debugging requires the tRPC DevTools panel.
Pricing
Free. Open source (MIT).
Best Use Cases
- Full-stack TypeScript apps (Next.js, Remix)
- Internal tools and dashboards
- Monorepos with shared types
- Rapid prototyping (skip API design)
Worst Use Cases
- Public APIs (use REST)
- Multi-language backends (use REST/GraphQL)
- Mobile apps with native clients (use REST)
- Microservices (use REST/gRPC)
FAQ
tRPC vs REST?
tRPC for internal full-stack TypeScript. REST for public APIs and multi-language clients.
tRPC vs GraphQL?
tRPC for same-team full-stack. GraphQL for multiple clients with different data needs.
Can I use tRPC with React Native?
Yes, if your React Native app uses TypeScript. tRPC works the same way.
Bottom Line
tRPC is the best way to build internal APIs for full-stack TypeScript apps in 2026. Zero API layer, instant type safety, React Query integration. The tradeoff: TypeScript-only and monorepo-preferred. For Next.js + TypeScript: use tRPC for internal APIs and REST for anything external.