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:
- 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.
- Schema governance means as your graph grows, schema discipline becomes a team-wide practice and not a one-time setup.
- Authorization complexity means REST's approach of protecting the endpoint is simpler than protecting each individual field on the graph.
- 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
.protofiles 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?




