The API layer decision gets debated endlessly in developer forums and rarely gets an honest treatment from the perspective that matters most: what does it cost to maintain, extend, and debug as a product grows?
This is that treatment. I've shipped production systems using all three. Here's the actual tradeoff matrix.
REST: Still the Right Default
REST is not exciting in 2026. It's also still the correct default choice for the majority of B2B SaaS products.
What REST does well:
- Every developer on your team knows it
- HTTP semantics (status codes, methods, caching) are understood and well-documented
- Excellent tooling for documentation (OpenAPI), testing (Postman, HTTPie), and observability
- Third-party integrations are straightforward — webhooks, external APIs, Zapier, all assume REST
The actual case against REST is not that it's bad. It's that the manual contract between client and server — the type definitions you write for the response shape, the runtime validation you add to avoid surprises — is overhead that TypeScript-first alternatives eliminate.
That overhead is worth it when:
- You have external consumers of your API (partners, integrations, public API users)
- You have a polyglot backend (some services not in TypeScript)
- You need HTTP caching semantics at the response level
tRPC: The Right Choice for Full-Stack TypeScript Teams
If your stack is TypeScript end-to-end — Next.js front end, Node/Bun/Deno backend — tRPC is worth serious consideration.
What tRPC gives you: a procedure-call abstraction where TypeScript types are the contract. Define a procedure on the server, call it on the client. TypeScript enforces the input and output shape at compile time. A server-side schema change propagates as a compile error on the client, not a runtime failure.
At Meridian Analytics, this was the architectural decision that had the most compound value over the 14-week build. The team made multiple schema changes post-launch. None of them produced runtime type errors — they were caught at compile time, fixed before deployment. The alternative (REST with hand-maintained type definitions) would have required discipline to keep in sync; tRPC makes sync automatic.
The case against tRPC:
- Tight coupling to TypeScript. If any part of your stack is not TypeScript, tRPC doesn't bridge it.
- Not suitable for external consumers. tRPC is an internal RPC mechanism, not a public API protocol. If you need to expose your API to partners or integrations, you'll need REST endpoints alongside tRPC or instead of it.
- Less familiar to engineers with primarily REST backgrounds — the learning curve is real, though short.
The pattern that works well: tRPC for all internal client-server communication, REST endpoints for webhooks and external integrations. These coexist cleanly.
GraphQL: Specific Use Cases, Specific Teams
GraphQL solves a specific problem: clients with heterogeneous data requirements that don't want to receive more data than they need, and where the server shouldn't need to know about every client's specific requirements.
This is genuinely the right solution for:
- Public APIs with external developers who build clients you can't anticipate
- Applications with multiple distinct client types (web, mobile, desktop) that query substantially different data shapes from the same backend
- Products where the data graph is genuinely complex and deeply interconnected
It's the wrong solution for:
- Internal B2B SaaS dashboards where the client requirements are known and controlled
- Teams that don't have GraphQL experience — the resolver model, caching semantics, and N+1 problem are non-trivial to get right
- Products where the over-fetching problem REST supposedly creates isn't actually a problem at your data volumes
The N+1 problem is worth naming explicitly. GraphQL queries that look clean in the schema can produce N+1 database queries at runtime. DataLoader solves this, but it's additional infrastructure that teams without GraphQL experience often add after they discover the problem in production, not before.
The Decision Framework
Choose REST if:
- You have or expect external API consumers
- Any part of your backend is not TypeScript
- Your team doesn't have strong TypeScript discipline
- You're building a public-facing API product
Choose tRPC if:
- Full TypeScript stack, client and server
- Internal-facing client-server communication (no external API consumers)
- You want type safety enforced at compile time rather than maintained by discipline
- Small to medium team where the tight coupling is a feature, not a risk
Choose GraphQL if:
- Public API with diverse external consumers
- Multiple client types with substantially different data requirements
- Team has GraphQL experience or strong motivation to build it
- Complex interconnected data graph where field-level selection matters
The hybrid that works: tRPC for internal client-server calls + REST for external webhooks and integrations. This is what I'd reach for on a greenfield TypeScript SaaS project in 2026. It captures the type safety benefits of tRPC for the 80% of API calls that are internal, while keeping REST's accessibility for the 20% that are external.
The Question Underneath the Question
The API layer debate often proxies a more important question: how will your team maintain the contract between client and server as the product evolves?
With REST, the answer is: discipline, documentation, and good testing. With tRPC, the answer is: TypeScript's compiler. With GraphQL, the answer is: schema governance and resolver ownership.
All three answers are legitimate. The right one depends on your team's composition, your product's data model, and whether you have external consumers. Choose deliberately, because the switching cost — particularly from REST to tRPC or vice versa — is real. It's not as expensive as a CRM migration, but it touches every API call in your application.
Make the decision once, on purpose, with the full tradeoff set in view.