Skip to content
← All writing
March 18, 2026·6 min read

REST vs GraphQL vs gRPC for App Backends

Every app needs a way for its front end to talk to its backend, and there are three dominant styles: REST, GraphQL, and gRPC. They're often argued about as if one is simply "modern" and the others "legacy" — GraphQL had years of being the inevitable future, gRPC gets pitched as what serious engineers use — but that framing is wrong and has led plenty of teams into complexity they didn't need. Each of the three solves a different problem well, and the right question isn't "which is best" but "who is calling this API, and what do they need?" This post compares them plainly so you can pick with intent.

REST: the dependable default

REST models your API as resources (users, orders, expenses) accessed over standard HTTP verbs: GET to read, POST to create, PUT/PATCH to update, DELETE to remove. A request to GET /users/42 returns that user as JSON, with an HTTP status code telling you how it went. That's most of the idea — and the simplicity is the point.

Strengths:

  • Universally understood. Every developer, tool, framework, and platform speaks REST. Onboarding is trivial; the questions ("what's the endpoint? what does it return?") have thirty years of shared vocabulary behind them.
  • Works with the web's plumbing. HTTP caching, status codes, load balancers, proxies, and CDNs all understand REST semantics natively — GET /articles/42 is cacheable by infrastructure that has never heard of your app.
  • Simple to build and debug. You can hit an endpoint from a browser or curl and see exactly what happens. When something breaks at 2 a.m., that transparency is worth a lot.

Weaknesses:

  • Over- and under-fetching. An endpoint returns a fixed shape. You often get more data than the screen needs (wasted bytes), or have to make several calls to assemble one view (wasted round trips — which mobile networks punish).
  • Endpoint sprawl. Complex apps accumulate specialized endpoints (/users/42/dashboard-summary), each solving one screen's fetching problem, until the API is a museum of past UI decisions.

Best for: most apps, most of the time. If you're unsure, start with REST — it's the lowest-risk choice, it's rarely the wrong one, and the pragmatic fix for its weaknesses (a purpose-built endpoint for your busiest screen) is usually an afternoon's work, not an architecture change.

GraphQL: the client asks for exactly what it needs

GraphQL exposes a single endpoint and a schema describing all available data. The client sends a query specifying precisely which fields it wants — including across relationships — and gets back exactly that shape:

query {
  user(id: 42) { name, expenses(last: 5) { amount } }
}

Strengths:

  • No over/under-fetching. The client requests one tailored payload for a whole screen, even when the data spans several related entities. On mobile, collapsing three round trips into one is a real, user-visible win.
  • Clients evolve without server changes. A new screen needing a different slice of existing data is just a new query — no new endpoint, no backend ticket, no waiting. This is why GraphQL genuinely shines for teams with many clients (iOS, Android, web, partners) iterating at different speeds against one API.
  • Strong typing and introspection. The schema documents itself and powers excellent tooling — autocompleted queries, generated types, mock servers.

Weaknesses:

  • More server complexity. You implement resolvers and must actively manage performance: the classic N+1 query problem (a query for a list innocently triggering a database hit per item) requires batching machinery, and because clients can compose arbitrary queries, you need query-depth and cost limits so nobody — malicious or just enthusiastic — asks for the whole graph at once.
  • HTTP caching is harder. Everything is a POST to one endpoint, so the web's built-in caching stops helping; caching moves into your application layer.

Best for: apps with rich, interrelated data and many client screens each needing different slices of it — and teams willing to invest in the server-side machinery. If your API has a handful of screens and one client, GraphQL's flexibility solves a problem you don't have, at a complexity price you still pay.

gRPC: fast, typed service-to-service calls

gRPC uses a compact binary format (Protocol Buffers) over HTTP/2. You define your services and messages in a .proto file, and code generation produces strongly-typed client and server stubs in essentially every language. Calls look like normal function calls; the network almost disappears from the code.

Strengths:

  • Performance. The binary protocol is smaller on the wire and much faster to serialize than JSON — meaningful when services exchange millions of messages, or when payloads are large and structured.
  • Contracts that can't drift. The .proto file is the single source of truth; client and server are generated from it, so a mismatch is a compile error rather than a production surprise. In a polyglot backend — Go here, Python there — this is the glue that keeps teams honest.
  • Streaming. First-class support for streaming in either direction, or both at once, without bolting on a second protocol.

Weaknesses:

  • Not browser-friendly. Browsers can't speak native gRPC; reaching web clients requires a proxy translation layer (gRPC-Web), which mostly cancels the simplicity. Mobile apps can use gRPC directly, but the tooling and debugging comfort lag well behind REST.
  • Less human-readable. Binary payloads mean you can't just eyeball what's on the wire; debugging needs tooling (grpcurl exists, but it's another thing to know).

Best for: internal, service-to-service communication in a multi-service backend, where performance and strict contracts matter and you control both ends. That's not a consolation prize — it's a huge and important niche — but it's a specific one.

How to choose

  • Building a typical app backend, or unsure? → REST. Boring is a feature; you can always add more later.
  • Many clients and screens, each needing different slices of rich, interrelated data — and the team capacity to run the machinery? → GraphQL.
  • Connecting internal services, need speed and compile-time contracts? → gRPC.

And note they aren't mutually exclusive — the most common serious architecture uses two of them deliberately: REST or GraphQL at the edge (what your app talks to, where ubiquity and debuggability matter) and gRPC between internal services behind it (where performance and contracts matter). Each layer uses the tool suited to who's calling it.

One more honest observation: API style is rarely why a backend succeeds or struggles. Clear resource design, consistent error shapes, solid auth, and good documentation matter more than which of the three you picked. A well-designed REST API beats a sloppy GraphQL one every time, and vice versa.

Summary

REST, GraphQL, and gRPC aren't a ranking — they're a toolbox. REST is the simple, universal default and the right call for most app backends. GraphQL earns its complexity when many clients need flexible, precisely-shaped data across many screens. gRPC excels at fast, strongly-typed communication between your own internal services. Choose based on who's calling the API and what they need, start with REST unless you have a concrete reason not to, and remember that mixing them by layer — friendly protocol at the edge, fast protocol inside — is not indecision; it's the pattern that most mature backends converge on.

Further reading