tRPC vs GraphQL: Which Should Choose
Cover photo by Conny Schneider on Unsplash
tRPC is better than GraphQL for most full-stack TypeScript projects. I say this because the productivity gains from end-to-end type safety without a schema generation step are massive.
Why the schema matters
GraphQL requires you to define a schema, write resolvers, and then generate types on your frontend. It is a rigorous process. If you have a large team where the backend and frontend are strictly separated by a contract, GraphQL makes sense. It acts as a gatekeeper.
But for a solo developer or a small team, that gatekeeper is just more chores. Here is what a simple tRPC router looks like in practice:
const appRouter = t.router({ getUser: t.procedure .input(z.string()) .query(({ input }) => { return { id: input, name: 'Namir' }; }),});Then you consume it directly in your React component without any manual type generation. It just works. The type is inferred automatically. I save hours every week just by skipping the codegen build step.
Where GraphQL actually wins
“But GraphQL allows me to fetch exactly the data I need, so I don’t over-fetch.” This is a fair point. If you are building a massive client-side application where you cannot control the backend, or you need to aggregate data from ten different microservices into one request, GraphQL is king. It is a fantastic tool for managing complex data dependency graphs. (I admit, sometimes I miss the ability to join related data across services without hitting the database three times.)
However, most apps are not Facebook. Most of us are building CRUD apps where the complexity is in the business logic, not in the data structure. Adding a GraphQL layer to a standard PostgreSQL database is often overkill. You are basically building a query engine for your frontend instead of just building features.
Should you switch?
If you are starting a new project in a monorepo, use tRPC. It creates a frictionless experience where your API feels like a function call. It is basically a distributed function call that happens to cross the network boundary.
If you find yourself needing to expose a public API to third parties, use REST. REST is simple to document and every developer on the planet knows how to use it. GraphQL is great, but it requires buy-in from both the server and the client to really shine. If the client doesn’t care about the flexibility, don’t force it upon them.
Sometimes I wonder if we over-engineer our data layers. Do I really need a formal query language to update a user’s email address? Probably not. Keep it simple until it hurts. If your project starts simple, it stays maintainable. What are you building that actually requires a custom query graph?