Over the past 5 years, GraphQL has established itself as the "modern" way to build APIs. Apollo, Relay, schema-first approaches — an entire ecosystem promising elegance and flexibility for every project. The reality for most SaaS and e-commerce products, however, is more modest: GraphQL adds complexity that rarely pays off.
In this article, I'll explain why on most new projects we use tRPC instead of GraphQL — and when GraphQL remains the right choice.
What GraphQL promises
- A single endpoint that returns exactly what the client requests (no over-fetching)
- Strongly typed schema shared between frontend and backend
- Standardized frontend tooling (Apollo, urql, codegen)
- Federation for microservice architectures
This sounds great on paper. In practice, for a single-team monolith project (which 90% of SaaS startups are), most of these benefits are not used or carry more cost than benefit.
What hurts with GraphQL
Codegen pipeline
Schema-first GraphQL means every API change requires regenerating types. This is an extra build step that must be maintained in CI, on every PR. With tRPC, types come directly from TypeScript inference — zero generation, zero CI complexity.
The N+1 problem
GraphQL makes it easy for the client to request nested data ("users { posts { comments { author } } }"). Without careful resolver implementation with DataLoader, these queries can hammer databases. The solution — DataLoader + batching layer — adds another complexity layer that junior developers often miss.
Authorization complexity
In a REST/tRPC context, authorization is at the endpoint level — clear and understandable. In GraphQL, authorization needs to be field-level and reason about nested resolvers. This is possible but requires libraries like GraphQL Shield and significantly more glue code.
What tRPC delivers
tRPC is a TypeScript-first library for building end-to-end type-safe APIs without code generation. You define functions on the server, call them directly on the client, and types come automatically.
- End-to-end type safety without a codegen step
- IDE autocomplete from backend procedures on the frontend
- Refactoring is trivial — rename a procedure on the server, TypeScript shows all client call sites
- No bundle bloat from a GraphQL client (Apollo is ~30KB gzip)
- Standard HTTP transport — easy CDN caching, REST inspection tools work
- Validation via Zod — same schema for input + types
Real example
In Frizmo Platform we use tRPC for all server-client communications. We have about 80 procedures organized in routers. Time from "I have an idea for a new feature" to "PR merged" is usually under 2 hours for CRUD operations.
When we started the project, we considered GraphQL. We calculated the additional hours for codegen setup, DataLoader implementations, resolver-level auth, Apollo Client configuration — about 30-40 hours for initial setup and another 5-10 hours per month maintenance. With tRPC these hours go directly into new features.
When GraphQL remains the right choice
- You have multiple front-end clients (web, iOS, Android, partners) with different data needs
- The API is public, used by external developers
- You have a microservice architecture with federation needs
- You work in a team of 50+ where contract-first development is necessary
- You have a complex data graph where clients genuinely need to query arbitrary nested structures
If none of these describe your project, GraphQL is likely over-engineering.
When neither tRPC nor GraphQL
For many projects, standard REST endpoints in Next.js API routes or server actions are enough. Especially for simple CRUD operations with little state. Not every project needs an RPC or query layer.
Conclusion
GraphQL is a powerful tool designed for specific complex scenarios. tRPC is a pragmatic solution for TypeScript-heavy products that do not need complex query needs.
The mistake I see most often is that teams pick GraphQL by default because it is fashionable. The result — weeks in build infrastructure instead of shipping features. For 90% of new projects, tRPC is the faster path to production without compromise in developer experience.