GraphQL vs REST vs tRPC: Choosing the Right API for Your App
Every web or mobile product needs a way for the front end to talk to the back end. For years the default answer was REST. Then GraphQL arrived, promising flexible queries, and more recently tRPC has won fans among TypeScript teams for its end-to-end type safety. Founders hear all three names in technical discussions and are asked to approve a choice they may not fully understand.
This guide explains each approach in plain language, shows where each shines, and gives you a decision process. The honest summary is that REST is the safest default, GraphQL solves specific problems well, and tRPC is excellent in a narrow but common situation.
REST: The Familiar Default
REST organizes an API around resources, such as /customers or /orders/123, and uses standard HTTP methods like GET, POST, PUT, and DELETE. Responses are usually JSON.
Strengths
- Universally understood. Nearly every developer, tool, and platform supports it.
- Works well with HTTP caching. Standard caches and CDNs understand GET requests.
- Simple to document and test. Tools like OpenAPI describe endpoints clearly, and you can test with a browser or basic client.
- Great for public APIs. Partners and third-party developers can integrate quickly.
Weaknesses
- Over-fetching and under-fetching. An endpoint may return more fields than a screen needs, or require several calls to assemble one view.
- Endpoint growth. As screens multiply, teams add custom endpoints, and the API can become inconsistent.
GraphQL: Ask for Exactly What You Need
GraphQL exposes a single endpoint and a typed schema. The client sends a query describing exactly which fields it wants, and the server returns just that shape.
Strengths
- Precise data fetching. One request can gather related data from several sources, which helps mobile apps on slow networks.
- Strong schema and tooling. Introspection, auto-generated documentation, and typed clients improve developer experience.
- Multiple clients, one API. A web app, an iOS app, and an Android app can each request the fields they need without new endpoints.
- Evolution without versions. Fields can be deprecated gradually rather than releasing a new API version.
Weaknesses
- More complexity on the server. Resolvers, batching, and caching require care to avoid performance traps such as the N+1 query problem.
- Harder HTTP caching. Because queries are usually POST requests to one endpoint, standard CDN caching is less straightforward.
- Security considerations. Deeply nested or expensive queries can overload your server unless you set depth limits, cost analysis, and rate limits.
- Learning curve. Small teams may spend time on tooling that a simple REST API would not need.
tRPC: Type Safety Without a Schema Language
tRPC lets a TypeScript front end call back-end functions as if they were local, with types shared automatically. There is no separate schema file or code generation step. If you change a back-end function's output, the front end shows a compile error immediately.
Strengths
- Excellent developer speed. Autocompletion and instant type errors catch bugs before runtime.
- Minimal boilerplate. No schema definition, no generated clients.
- Ideal for full-stack TypeScript teams. Particularly when the front end and back end live in one repository.
Weaknesses
- TypeScript only. A mobile app written in Swift, Kotlin, or Dart cannot share the types directly.
- Tight coupling. The client and server are designed to evolve together, which is awkward for public or third-party APIs.
- Less suited to outside consumers. There is no standard contract for partners to read.
A Real-World Example
Consider an illustrative scenario: a startup builds a web dashboard and a companion mobile app for field technicians. The dashboard is built in Next.js by a small TypeScript team. The mobile app needs to work on weak connections and show a screen that combines a job, its customer, equipment history, and notes.
The team might choose tRPC for the web dashboard, since the same developers own both sides and move fast with shared types. For the mobile app, they could expose a lean REST or GraphQL API so that the mobile client, written in a different language, has a stable contract. If the mobile screens frequently need data combined from several entities, GraphQL could reduce the number of round trips. If the screens are simple, a few well-designed REST endpoints, possibly with a purpose-built "aggregate" endpoint for the job screen, might be enough and easier to cache. The point is that one product can legitimately mix approaches, as long as each choice has a reason.
Cross-Cutting Concerns That Matter More Than the Label
Regardless of the style you pick, several practices determine whether your API is pleasant to work with and safe to run:
- Authentication and authorization. Check permissions on the server for every request, never trusting the client.
- Rate limiting. Protect against abuse and accidental overload. Our article on API rate limiting and abuse protection for SaaS covers practical designs.
- Versioning and compatibility. Mobile users often run old app versions for months, so breaking changes hurt. Read about API versioning and backward compatibility for mobile apps before you ship.
- Consistent errors. Return clear, structured error responses so clients can handle failures gracefully.
- Observability. Log requests, measure latency, and trace slow calls.
- Documentation. Even internal APIs deserve a living reference.
Step-by-Step: Choosing the Right API Style
- List your clients. Identify every consumer: web app, iOS, Android, partner integrations, internal tools, and future ones.
- Check language boundaries. If every client and the server use TypeScript, tRPC becomes a serious candidate. If not, look at REST or GraphQL.
- Decide if the API is public. Public or partner-facing APIs benefit from REST's familiarity and clear documentation.
- Map your heaviest screens. Note which views need data from many entities and how sensitive they are to latency.
- Assess your team's experience. Pick what your developers can build and operate well. A familiar approach delivered cleanly beats a trendy one done poorly.
- Plan caching. Decide what can be cached at the CDN, server, or client, and confirm your approach supports it.
- Define security controls. Set authentication, rate limits, and, for GraphQL, query depth and cost limits.
- Prototype one hard screen. Build the most demanding view in your top candidate and review code size, speed, and clarity.
- Write down the decision. Record the reasons and the conditions under which you would revisit it.
Key Benefits of Choosing Deliberately
- Faster delivery. The right fit removes friction between front end and back end teams.
- Better mobile experience. Fewer round trips and smaller payloads make apps feel faster on slow networks.
- Safer releases. Type checking and clear contracts catch mismatches before users do.
- Lower maintenance cost. A consistent API style is easier to extend and to onboard new developers into.
- Easier integrations. A clear external contract opens doors to partners and automation tools.
Common Mistakes
- Adopting GraphQL for a simple app with a handful of screens, then maintaining unnecessary infrastructure.
- Using tRPC for an API that outside developers or non-TypeScript clients must consume.
- Designing REST endpoints around database tables instead of around the tasks users perform.
- Skipping query cost limits in GraphQL and leaving the door open to expensive requests.
- Changing response shapes without a deprecation plan, which breaks older mobile versions.
- Choosing based on hype instead of the actual shape of your product and team.
Performance and Caching in Practice
API style affects speed in ways that are easy to miss. With REST, a GET request for a product list can be cached by a CDN for minutes, so thousands of users share one server response. With GraphQL, caching usually moves into the client library and the server, using techniques like persisted queries, response caching by field, and batching of database calls. With tRPC, you typically rely on client-side caching libraries and server-side caching you build yourself. None of these is better in every case, but you should know in advance where caching will happen so that your busiest screens do not hit the database on every request.
Also think about payload size and the number of requests on a cold app start. A mobile screen that makes six sequential calls feels slow even if each call is fast. Combining calls with a purpose-built endpoint, a GraphQL query, or a batched tRPC request can improve perceived speed more than any server tuning.
Testing and Documentation
Whatever you choose, write contract tests that confirm responses keep the shape clients expect. For REST, an OpenAPI file can drive automatic tests and generate client code. For GraphQL, schema checks in your build pipeline can flag breaking changes before release. For tRPC, the compiler does much of this work, though you should still test behavior and authorization. Good documentation is part of the product: new developers, QA engineers, and automation specialists all move faster when they can read a clear description of what the API does.
Our Practical Recommendation
For most early-stage products, we suggest starting with a well-designed REST API, or tRPC if the entire stack is TypeScript and no outside clients are expected. Add GraphQL when you have several different clients with distinct data needs, or when over-fetching clearly hurts your mobile experience. If you are building a mobile-first product, our mobile app development service includes API design as part of the architecture phase, so the contract between app and server is planned before screens are built.
Conclusion
There is no universally best API style, only the one that suits your clients, team, and growth plans. REST offers familiarity and caching, GraphQL offers precise queries across many clients, and tRPC offers outstanding speed within a TypeScript stack. Start with your real requirements, prototype the hardest screen, and protect whichever approach you choose with strong security, rate limits, and a sensible versioning plan. A thoughtful API decision pays off in every sprint that follows.
Frequently Asked Questions
- Is GraphQL better than REST?
- Not always. GraphQL helps when several clients need different data shapes, while REST is simpler, widely understood, and easier to cache.
- When is tRPC a good choice?
- When your front end and back end are both TypeScript and no outside developers need to consume the API.
- Does GraphQL have security risks?
- Deeply nested or expensive queries can overload servers, so set depth limits, cost analysis, and rate limits.
- What is best for a public API?
- REST is usually best for public or partner APIs because of its familiarity, documentation tools, and HTTP caching.
- Can I mix API styles in one product?
- Yes. Many teams use one style for an internal web app and another for mobile or partner clients, as long as each choice has a clear reason.