GraphQL and tRPC both solve the same underlying problem — how do you keep the client and server in sync without hand-writing types twice — but they arrive at completely different answers. Picking the wrong one means either fighting the framework for months or rebuilding the API layer when you scale. This is the 2026 guide that skips the marketing and gives you the honest tradeoffs.
What changed in 2026
- tRPC v11 landed subscriptions and server-sent event streaming, making it a credible alternative for real-time features that previously forced teams to GraphQL.
- GraphQL Composite Schema (the successor to Federation v2) reached GA at major vendors, improving multi-team API composition but also adding a new layer of config.
- React Server Components changed the calculus — when data fetching moves to the server, the RPC-style of tRPC fits more naturally than a client-driven query language.
- AI code assistants now generate both fluently; boilerplate cost is no longer a differentiator — design cost is.
The core distinction
tRPC is a TypeScript-native RPC layer: you define procedures in TypeScript, and the client gets full type inference with no code generation step. It only works when client and server share a TypeScript codebase (monorepo or published package).
GraphQL is a query language with a schema that is language-agnostic. Clients can be in any language, can request exactly the fields they want, and can introspect the schema at runtime. The type safety story requires code generation (GraphQL Code Generator, Pothos, etc.).
Feature comparison
| Feature |
tRPC v11 |
GraphQL (Apollo/Yoga) |
| Type safety |
Automatic inference |
Code-gen required |
| Language support |
TypeScript only |
Any language |
| Multiple clients |
Hard (same TS repo) |
First-class |
| Subscriptions |
Yes (v11+) |
Yes (WebSocket / SSE) |
| Schema introspection |
No |
Yes |
| Federation / composition |
No |
Yes (Composite Schema) |
| N+1 problem |
Easier to avoid |
Requires DataLoader |
| Bundle size (client) |
~4 KB |
~32 KB (Apollo) |
| Learning curve |
Low |
Moderate–High |
When tRPC is the right call
- Full-stack TypeScript monorepo (Next.js, Remix, SvelteKit with a TS API) — you get end-to-end types with near-zero ceremony.
- Small team, single front-end — one team owns both sides; no need for a schema registry or federation.
- Rapid iteration — refactor a server procedure and TypeScript shows every broken call site instantly.
- Server Components + tRPC work naturally: call procedures directly on the server, pass typed data to client components.
// server/routers/post.ts
export const postRouter = router({
list: publicProcedure
.input(z.object({ cursor: z.number().optional() }))
.query(async ({ input }) => {
return db.post.findMany({ take: 20, skip: input.cursor ?? 0 });
}),
});
// client — fully typed, no codegen
const { data } = trpc.post.list.useQuery({ cursor: 0 });
// ^? Post[] inferred automatically
When GraphQL is the right call
- Multiple clients (web, mobile, third-party) with different field requirements.
- Multiple back-end teams each owning a subgraph — Federation composes them into one schema.
- Non-TypeScript consumers — mobile apps in Swift/Kotlin, data pipelines in Python.
- Public API where introspection, tooling (GraphiQL, Postman), and versioning matter.
# schema.graphql
type Post {
id: ID!
title: String!
author: User!
tags: [Tag!]!
}
type Query {
posts(first: Int, after: String): PostConnection!
}
How to pick
- Single TS codebase, single team? → tRPC, full stop.
- Multiple languages or public API? → GraphQL.
- Multiple back-end teams composing schemas? → GraphQL Federation.
- Mostly server-side rendering (RSC)? → tRPC or plain
fetch; a query language adds friction.
- Real-time subscriptions in 2026? → Either; tRPC v11 closes the gap.
Common mistakes
Choosing GraphQL for a private BFF. A Back-For-Frontend only consumed by your own Next.js app gets all the overhead (codegen, persisted queries, resolver tracing) with none of the multi-client benefit. tRPC or plain server actions are simpler.
Ignoring N+1 in GraphQL. Every nested author field on a Post list fires a separate DB query without DataLoader. Instrument your resolvers before going to production.
Under-investing in GraphQL schema governance. Without a schema registry and breaking-change checks in CI, schema drift breaks clients silently. Tools like GraphQL Inspector belong in your pipeline from day one.
Assuming tRPC scales poorly. tRPC is HTTP underneath; it scales exactly the same as your server. The monorepo assumption is the real constraint, not performance.
What to skip
- GraphQL for internal microservice-to-microservice calls — gRPC or plain JSON HTTP is faster and simpler.
- Apollo Client on the front-end if you use tRPC — you get the same normalized cache benefit from React Query, which tRPC wraps.
- Schema stitching (the old pre-Federation approach) — it was deprecated; use Composite Schema instead.
FAQ
Can I migrate from REST to tRPC incrementally?
Yes. tRPC supports adapters for Express and Fastify, so you can expose new routes as tRPC procedures while keeping existing REST endpoints running side by side.
Does GraphQL work with React Server Components?
It works but feels awkward — RSC prefer async/await fetch patterns, and a query language adds a layer of indirection. Most teams using RSC reach for tRPC or plain server actions.
Is tRPC production-ready for large apps?
Yes — Vercel, Clerk, and many other teams run it in production at scale. The constraint is the TypeScript monorepo requirement, not reliability.
What about REST in 2026?
REST is still fine for simple CRUD APIs. tRPC vs GraphQL is the decision when you want typed contracts; REST is the choice when simplicity and broad tooling compatibility matter more.
Where to go next