Skip to main content

Command Palette

Search for a command to run...

The API Decision That Haunts Your Architecture

Published
8 min read
The API Decision That Haunts Your Architecture

Series: Backend Engineering Fundamentals · Post 01 of 07 Level: Intermediate · Read time: ~8 min


A team I know spent nine months migrating their mobile backend from REST to GraphQL. Two engineers dedicated full-time. At the end of it, their core performance problem of slow dashboard loads was unchanged. The culprit was N+1 queries in the database layer they had never touched.

API decisions feel reversible. They rarely are. By the time you have built client SDKs, versioning contracts, and downstream integrations, switching paradigms is a full rewrite. That is why your API style is an architectural decision and not an implementation detail.

Let us break down the four major API paradigms honestly so you can choose based on your actual constraints and not the current hype cycle.


The Four Paradigms at a Glance

Aspect REST SOAP GraphQL gRPC
Protocol HTTP HTTP, SMTP HTTP HTTP/2
Data Format JSON, XML XML (strict) JSON Protobuf (binary)
Flexibility Medium Low and rigid High and client-driven Medium with strong contract
Performance Moderate Heavy due to XML overhead Good as it avoids over-fetching Excellent with binary and streaming
Team Overhead Low High Medium Medium
Best For Web and mobile APIs Enterprise and legacy Complex UI data needs Internal microservices

REST — The Default, For Good Reason

REST (Representational State Transfer) is stateless, resource-based, and uses standard HTTP methods: GET, POST, PUT, DELETE. JSON is the lingua franca. Almost every developer knows it, every framework supports it, and tooling is mature.

The real strength: REST's simplicity is the feature itself. Low cognitive overhead means faster onboarding, easier debugging, and predictable behavior in production.

The honest weakness: REST can lead to over-fetching where the response contains more fields than the client needs, or under-fetching where the client needs multiple round trips to assemble a view. For most teams this is manageable. For teams with high-traffic and data-heavy mobile apps it becomes real latency.

GET /api/v1/users/123
GET /api/v1/users/123/orders
GET /api/v1/users/123/preferences
# Three requests to build one profile page

Use REST when:

  • You are building a public-facing API
  • Your team is mixed seniority or onboarding quickly
  • You need broad tooling, documentation, and ecosystem support
  • You do not yet know all the ways your data will be consumed

SOAP — Not Dead, Just Misunderstood

SOAP has a deserved reputation for verbosity, WSDLs are painful, and XML parsing is heavy. And yet it still runs banking systems, healthcare integrations, and government infrastructure worldwide.

Why? Because SOAP has built-in standards for things REST leaves entirely to you:

  • WS-Security for message-level encryption and signing
  • WS-AtomicTransaction for distributed transaction support
  • WSDL contracts that are machine-readable, strongly typed, and version-controlled
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Body>
    <pay:ProcessPayment>
      <pay:AccountId>ACC-9821</pay:AccountId>
      <pay:Amount>500.00</pay:Amount>
    </pay:ProcessPayment>
  </soapenv:Body>
</soapenv:Envelope>

If you are integrating with a payment processor, a hospital EMR system, or any legacy enterprise platform built before 2010, you are using SOAP whether you planned to or not.

Use SOAP when:

  • You are in a compliance-heavy domain such as finance, healthcare, or government
  • You are integrating with enterprise systems that mandate it
  • You need formal, auditable contracts and built-in security standards

GraphQL — Powerful, But Only With Discipline

GraphQL flips the data fetching model. Instead of the server defining what data an endpoint returns, the client declares exactly what it needs. One request, precisely the data you asked for, nothing more.

query {
  user(id: "123") {
    name
    email
    orders(last: 5) {
      id
      total
      status
    }
  }
}

This is genuinely powerful for complex UIs such as dashboards, news feeds, and mobile apps where different views need different data shapes.

The honest tradeoffs:

  1. N+1 query problem means naive GraphQL resolvers can fire a database query per item in a list. You need DataLoader or similar batching patterns to fix this.
  2. Schema governance means as your graph grows, schema discipline becomes a team-wide practice and not a one-time setup.
  3. Authorization complexity means REST's approach of protecting the endpoint is simpler than protecting each individual field on the graph.
  4. Caching means HTTP-level caching is trivial with REST but with GraphQL's POST-based queries you need persisted queries or custom caching layers.
# Without DataLoader: N+1 queries for 100 orders
type Query {
  orders: [Order]  # 1 query
}
type Order {
  customer: User  # 100 queries, one per order
}

Use GraphQL when:

  • Your frontend teams are strong and own the client-side queries
  • You have multiple clients such as mobile, web, and partner apps needing different data shapes
  • Your backend team can invest in schema governance and query depth limiting
  • You are building a product and not a generic public API

gRPC — The Microservices Workhorse

gRPC uses Protocol Buffers (Protobuf), a binary serialization format, over HTTP/2. The result is significantly faster than JSON over HTTP/1.1, with native support for streaming in both directions.

// Define your contract in .proto
service OrderService {
  rpc GetOrder (OrderRequest) returns (OrderResponse);
  rpc StreamOrders (Empty) returns (stream Order);
}

message OrderRequest {
  string order_id = 1;
}

The contract is defined in .proto files that generate client and server code in multiple languages. This makes gRPC exceptional in polyglot environments where your Go service and your Python service speak the same typed contract.

The tradeoffs:

  • Browser support is limited and requires a gRPC-Web proxy for browser clients
  • Protobuf binary is not human-readable and is harder to debug without proper tooling
  • Managing .proto files adds workflow overhead for smaller teams

Use gRPC when:

  • You are building internal service-to-service communication
  • Performance and latency are critical
  • You have or expect a polyglot architecture
  • You need bidirectional streaming for real-time data or event feeds

How to Actually Choose

Stop asking which API is best. Start asking what your team needs to operate reliably at your current scale.

Your situation Recommended
Public API, general purpose, mixed team REST
Complex UI, multiple client types, strong frontend GraphQL
Internal microservices, high throughput, polyglot gRPC
Enterprise integration, compliance-driven, legacy SOAP
Mobile app with internal services REST externally, gRPC internally
Startup moving fast with a small team REST until it hurts

Most mature systems use more than one paradigm. REST for the public API, gRPC for the service mesh, webhooks for async consumers. There is no rule against mixing paradigms, just the cost of maintaining each one.


Three Pitfalls to Avoid

1. Migrating for hype and not for pain

GraphQL and gRPC are excellent but adopting them before you have the problem they solve is expensive. Slow queries? Fix the database. Mobile over-fetching on five endpoints? REST versioning or sparse fieldsets might be cheaper than a full migration.

2. Treating API design as a junior task

API contracts outlive the engineers who write them. Resource naming, versioning strategy, and error envelope structure all become your team's debt or foundation depending on the care taken in the first sprint.

3. One style for everything

A GraphQL API that also powers your internal service mesh is the wrong tool in the wrong place. Match the paradigm to the use case, even if that means maintaining two styles.


Key Takeaways

  • REST is still the right default and you should not fix what is not broken
  • GraphQL pays off when UI needs are complex and your team has schema discipline
  • gRPC wins for internal microservice communication at scale
  • SOAP survives because enterprise compliance demands it and that reality deserves respect
  • Most production systems run multiple API styles so match the tool to the context
  • The best API is the one your team can build, operate, and debug at 2am

What is an API migration you have lived through? Did switching paradigms solve the original problem or did it just reveal a different one?

Drop your thoughts in the comments. The best real-world examples will make it into Part 2.


Next in the series: Post 02 — Cache Invalidation, The Problem That Humbles Everyone

After you have decided how clients talk to your system, the next question is what do you do when those requests are expensive?

Backend Engineering Fundamentals

Part 6 of 6

Backend systems don't fail because of bad code alone — they fail because of bad decisions. This series breaks down the foundational concepts every developer, architect, and engineer needs to build systems that scale, stay secure, and survive production: APIs, caching, security, databases, message queues, scalability, and observability. No fluff, no vendor pitches — just the tradeoffs that actually matter.

Start from the beginning

You Can't Manage What You Can't See: The Three Pillars of Observability

Series: Backend Engineering Fundamentals · Post 07 of 07 Level: Intermediate · Read time: ~9 min It's 2am. An alert fires. Your service is down. You open your dashboard: CPU is fine, memory is fine.