Oleg Koval markOleg Koval

NEWSLETTER

How Distributed Systems Really Talk: REST, gRPC, GraphQL, and tRPC in Practice

REST, GraphQL, gRPC, and tRPC are not just protocol choices. They are political choices between teams. Here is when each one works best, and when it will fight you.

By Oleg Koval·
Image: How Distributed Systems Really Talk: REST, gRPC, GraphQL, and tRPC in Practice

Also on Substack

REST, GraphQL, gRPC, and tRPC are not just protocol choices. They are political choices between teams. Here is when each one works best, and when it will fight you.

When you scale from a handful of backend services to a network of teams shipping independently, you discover something quickly: APIs are politics.Distributed systems are rarely brought down by storage engines or consensus protocols. More often, teams trip over something far more mundane: how services talk to each other.

Every endpoint, response code, and contract revision is a small treaty between teams.

Over the past two decades, we’ve accumulated a surprising number of “languages” for machines to negotiate with. Each emerged for a reason, solved a pain, and introduced new ones. Below is a practical, engineer-first view of how these protocols evolved, where they shine, and where they are the wrong tool.

Thanks for reading oleg’s Substack! Subscribe for free to receive new posts and support my work.


1. HTTP: The Common Language We Still Rely On

HTTP/1.1 remains the most widely adopted communication layer in distributed systems.

It works because it is:

  • simple

  • predictable

  • well-understood

  • universally supported

It gave us a shared vocabulary:

  • GET – retrieve

  • POST – create

  • PUT – replace

  • DELETE – remove

And a tone of voice through status codes:

  • 200: all good

  • 404: not here

  • 500: your server caught fire

Despite its age, HTTP continues to be the stable foundation on which everything else is built. You can replace frameworks. You cannot replace HTTP.


2. REST: When APIs Became Civilized

REST was not created to be “yet another API style.”

It was created to impose order.

Roy Fielding’s dissertation argued that networked systems should revolve around resources (nouns), not verbs:

Code3 lines
GET /orders/123
POST /users
DELETE /sessions/42

REST gave teams:

  • statelessness

  • predictable semantics

  • cacheability

  • standardization

This model worked exceptionally well for public APIs and internal boundaries where stability mattered more than speed or flexibility.

The biggest complaint? Overfetching.

REST returns the full representation of a resource, even when the client wants two fields.

Where REST is a good fit

  • Example: Stripe / GitHub style public APIs.

    You have thousands of unknown client implementations. You need:

    • stable URLs

    • long-lived versions

    • excellent HTTP caching and tooling

    • easy curl-level debugging

    Exposing this as gRPC would lock out many clients. Doing it as GraphQL would complicate caching and error handling for every integrator.

  • Example: Internal “platform” APIs between teams.

    A platform team provides /customers, /invoices, /subscriptions to multiple internal consumer teams. REST keeps contracts stable and debuggable and lets teams evolve at different speeds.

Where REST is usually the wrong tool

  • Your mobile app makes 6–8 REST calls to render a single screen, overfetching large payloads. Latency and data usage are now a product problem.

  • Your frontend needs to assemble data from 4–5 services per view. You start building ad-hoc “backend for frontend” aggregation endpoints to paper over REST limitations. At this point, GraphQL is often a better abstraction.


3. SOAP, RMI, CORBA: The Empires That Collapsed

Before REST, enterprise engineering tried RPC over heavy, rigid contracts:

  • RMI

  • CORBA

  • SOAP

They assumed distributed systems should behave like local function calls.

It looked nice on whiteboards and failed in practice:

  • systems tightly coupled across languages and vendors

  • brittle XML contracts

  • versioning nightmares

  • huge payloads

These approaches taught the industry one important lesson:

When your service boundary looks like a shared class, you’re building a distributed monolith.

Where SOAP still makes sense

  • Example: Legacy payment, insurance, telecom backends.

    If you integrate with a 20-year-old core system that only speaks SOAP, you use it. You do not rewrite the bank.

    In practice:

    • wrap SOAP calls with a thin internal REST/gRPC façade

    • keep SOAP at the edge of your architecture

Where SOAP is the worst option

  • Greenfield product, 2025, building a public API for a web and mobile client. Choosing SOAP here is self-sabotage. REST, GraphQL, or gRPC will all be strictly better in tooling, performance, and developer experience.


4. GraphQL: The Negotiator

Facebook developed GraphQL because frontend teams were tired of REST returning massive payloads.

GraphQL flipped the contract upside down:

  • The client chooses the shape of the data.

  • The server must comply.

Example:

Code7 lines
query {
  shipment(id: 42) {
    origin
    destination
    fuelUsed
  }
}

Benefits:

  • precise data retrieval

  • strong types

  • single endpoint

Costs:

  • complex caching rules

  • backend resolvers easily turn into N+1 factories

  • schema ownership shifts toward frontend teams

  • more infra and operational overhead

GraphQL is great when UI teams outnumber backend teams and iteration speed matters more than backend control.

Where GraphQL is a good fit

  • Example: Shopify Storefront API / GitHub GraphQL API.

    You have:

    • rich, variable UIs

    • many different views on the same underlying data

    • clients that evolve rapidly (web, iOS, Android)

    A product team can iterate on new views without requesting new backend endpoints each time. The graph becomes a stable domain model.

  • Example: Mobile apps under tight latency and bandwidth constraints.

    Rather than chaining multiple REST calls per screen, a single GraphQL query can hydrate the whole UI with only required fields.

Where GraphQL is the wrong tool

  • Internal service-to-service communication where:

    • calls are small and frequent

    • data is well-defined

    • latency is critical

    Here, GraphQL adds complexity for little benefit. gRPC or simple REST is usually cleaner.

  • Simple public APIs where you:

    • need strong HTTP caching

    • want easy adoption by scripting languages and low-code tools

    For this, REST is easier to consume and operate.


5. gRPC: The High-Performance Hotline

As systems grew, some teams needed:

  • speed

  • type safety

  • bi-directional streaming

  • efficiency over public accessibility

gRPC (Google’s modern RPC over HTTP/2) filled that gap.

Proto files define strict typed contracts:

Code3 lines
service TradeService {
  rpc InitiateTrade(TradeRequest) returns (TradeResponse);
}

Strengths:

  • binary, compact

  • extremely fast

  • codegen for many languages

  • great for internal service-to-service calls

Tradeoffs:

  • harder to debug over the wire

  • not ideal for random third-party clients

  • browser support requires gRPC-Web and extra infra

Where gRPC is a good fit

  • Example: Internal trading / risk / pricing microservices.

    Latency and throughput matter. Call patterns are well-known. You control both client and server. gRPC shines:

    • typed contracts in proto

    • streaming support

    • efficient binary transport

  • Example: ML inference and data pipelines.

    Services exchanging structured, binary payloads (features, embeddings, model configs) at high volume. REST adds overhead with no benefit.

Where gRPC is the wrong tool

  • Public APIs aimed at:

    • small integrators

    • customers using Python, PHP, or low-code platforms

    • scripting from curl / Postman

    For them, REST is dramatically easier. They do not want to deal with proto files and language-specific stubs.

  • Browser-only clients without gRPC-Web. You will end up building HTTP/JSON shims anyway.


6. tRPC: The TypeScript Shortcut

Teams building full-stack TypeScript apps (Next.js, Node) realized something:

“Why maintain two schemas—one for backend, one for frontend—if both sides already use TypeScript?”

tRPC removed the boundary entirely:

  • no schema files

  • no codegen

  • types flow automatically through the stack

It works exceptionally well when:

  • the entire system is TypeScript-based

  • you control both sides of the API

  • you want fast iteration over long-term stability

Where tRPC is a good fit

  • Example: B2B SaaS admin / dashboard app.

    A small team owns both:

    • Next.js frontend

    • Node backend

    They need to ship features fast and keep type safety. tRPC:

    • removes duplication of DTOs

    • keeps API and UI in lockstep

    • makes refactors safer and faster

  • Example: Internal tools and ops consoles.

    You do not need public, long-lived contracts. You just want:

    • fewer layers

    • fewer generated artifacts

    • fewer places for types to drift

Where tRPC is the wrong tool

  • You need a public API for third-party customers.

    Those customers are not on your TypeScript stack. They cannot “benefit” from your shared types. REST or GraphQL is better.

  • You expect to migrate parts of the stack to Go, Rust, or Java.

    tRPC becomes a liability once the system is no longer TypeScript-only.


7. API Gateways: Bureaucracy That Scales

Once an organization has enough services, APIs need governance.

This is where API gateways and service meshes come in.

Gateways handle:

  • authentication

  • rate limits

  • versioning

  • routing

  • monitoring

Service meshes handle:

  • mTLS

  • retries

  • service discovery

  • request shaping

This structure turns chaotic service communication into enforceable policy.

Real-world patterns

  • Example: Public-facing platform API.

    Your customers hit api.company.com. Behind that:

    • REST for public endpoints

    • GraphQL for internal BFFs

    • gRPC between internal services

    The gateway hides the mess and enforces auth, quotas, and deprecation.

  • Worst case: “Gateway as dumping ground.”

    Every time a client needs something slightly different, someone adds another ad-hoc transformation in the gateway.

    You end up with a brittle, undocumented ball of logic that should have been in well-owned services or BFFs.


8. When to Use What: A Practical Decision Table

Protocol Best For Strength Weakness REST Public APIs, clear boundaries Stable, cacheable, simple Overfetching, weak contracts GraphQL Rich frontends, many data sources Flexible queries, single endpoint Complex caching, heavier infra gRPC Internal microservices Fast, typed, streaming Harder to debug, public-hostile tRPC Full-stack TS apps End-to-end types, no schema drift TS-only, not for 3rd parties SOAP/RMI Legacy enterprise integration Strict contracts, existing infra Heavy, outdated for greenfield

9. The Real Lesson

APIs are not technology choices.

They are alignment choices.

The real job is ensuring that:

  • teams agree on contracts

  • versioning is respected

  • breaking changes are controlled

  • services remain independently deployable

  • communication remains predictable

Distributed systems fail on semantics long before they fail on transport.

The better the protocol, the easier the negotiation.

But the negotiation is unavoidable.


10. Final Thought

The industry keeps inventing new ways for machines to communicate.

  • REST will not die.

  • GraphQL will not replace REST.

  • gRPC will not replace GraphQL.

  • tRPC will not replace anything outside TypeScript ecosystems.

Each solves a different piece of the same problem:

How do we get independent systems—and teams—to understand each other well enough to ship?

In that sense, HTTP really is the diplomacy layer of modern software. Everything else is negotiation technique.

Thanks for reading oleg’s Substack! Subscribe for free to receive new posts and support my work.