GraphQL vs tRPC
A spec that works everywhere vs a TypeScript hack that's absurdly productive. The right answer depends on your team size.
tRPC
For TypeScript full-stack teams, tRPC eliminates an entire category of bugs (API contract mismatches) with zero code generation and zero schema files. GraphQL is more powerful but that power comes with a complexity tax most teams don't need to pay.
The Type Safety Revolution
Both tools solve the same problem: making sure your frontend and backend agree on API shapes. GraphQL does it with a schema language and code generation. tRPC does it with TypeScript inference.
GraphQL is a specification — language-agnostic, widely adopted, with a massive ecosystem. tRPC is a TypeScript library — opinionated, minimal, and only works in TypeScript/JavaScript land.
The tradeoff: GraphQL works everywhere. tRPC works amazingly in one ecosystem.
Where tRPC Wins: Developer Velocity
Define a procedure on the backend, call it from the frontend with full type safety. No schema files. No code generation step. No GraphQL fragments. Just TypeScript.
`typescript
// Backend
const appRouter = router({
getUser: procedure.input(z.string()).query(({ input }) => db.user.find(input)),
});
// Frontend — fully typed, autocomplete works
const user = trpc.getUser.useQuery('user-123');
`
Change the backend return type, and the frontend immediately shows a type error. No graphql-codegen step, no stale types. The feedback loop is instant.
For small-to-medium teams building Next.js or similar full-stack apps, tRPC removes an entire layer of API ceremony. Ship features, not schemas.
Where GraphQL Wins: Scale and Universality
GraphQL is language-agnostic. Your frontend is React, your backend is Go, your mobile app is Swift? GraphQL works. tRPC is TypeScript-only.
GraphQL's query language lets clients request exactly the data they need. No over-fetching. This matters for mobile apps on slow networks and for APIs serving diverse clients.
The ecosystem is enormous: Apollo, Relay, Hasura, PostGraphile, AWS AppSync. GraphQL Federation enables stitching multiple service schemas together — essential for large organizations with many backend teams.
For public APIs, GraphQL provides a self-documenting contract that external developers can explore with tools like GraphiQL or Apollo Studio.
The Complexity Question
GraphQL's flexibility is also its curse. N+1 query problems, cache invalidation complexity, file upload hacks, the waterfall problem with nested resolvers — these are real issues that every GraphQL team eventually fights.
GraphQL requires tooling: code generation (graphql-codegen), schema management, resolver organization patterns. The "setup tax" before you write your first query is non-trivial.
tRPC has almost zero setup: install the package, define a router, wrap your app. The entire architecture is 50 lines of boilerplate. The tradeoff is that you lose GraphQL's query language — every procedure call is a full RPC, not a flexible graph traversal.
If You're Starting Today
TypeScript full-stack (Next.js, T3 stack): tRPC. The productivity gain is massive and you won't need GraphQL's features until you're much bigger.
Multi-language backend or public API: GraphQL. The language-agnostic schema and client flexibility are necessary.
Team of 50+ developers with multiple backend services: GraphQL with Federation. tRPC doesn't scale to multi-team architectures.
Simple CRUD API: Honestly, just use REST. Neither GraphQL nor tRPC is necessary for fetching a list of items.
Quick Comparison
| Factor | GraphQL | tRPC |
|---|---|---|
| Type Safety | Code generation required | Native TypeScript inference |
| Setup Complexity | Schema + codegen + resolvers | ~50 lines of boilerplate |
| Language Support | Any language | TypeScript/JavaScript only |
| Query Flexibility | Client picks fields | Fixed procedures |
| Multi-service Architecture | Federation (mature) | Limited |
| Ecosystem | Apollo, Relay, Hasura, etc. | Small (growing) |
| Developer Velocity | Moderate (tooling overhead) | High (zero codegen) |
| Public API Suitability | Excellent (self-documenting) | Poor (TypeScript-only clients) |
The Verdict
Use GraphQL if: You're building a public API, have multi-language services, need query flexibility for mobile clients, or your organization has multiple backend teams.
Use tRPC if: You're a TypeScript full-stack team, want maximum velocity, and your API serves a single frontend (or multiple TypeScript clients).
Consider: REST with OpenAPI/Zod validation is a solid middle ground — language-agnostic like GraphQL, simpler than both.
For TypeScript full-stack teams, tRPC eliminates an entire category of bugs (API contract mismatches) with zero code generation and zero schema files. GraphQL is more powerful but that power comes with a complexity tax most teams don't need to pay.
Related Comparisons
Disagree? nice@nicepick.dev