REST vs SOAP vs GraphQL — Unbounded Queries Crash DB
Database connections spike to max with 'too many clients already' when GraphQL lacks depth limits.
- REST is resource-based, CRUD over HTTP with stateless operations
- SOAP is protocol-based, uses XML envelopes and strict WSDL contracts
- GraphQL is query-based, single endpoint with client-defined responses
- REST: ~30% overhead from over-fetching; GraphQL eliminates this but adds query cost analysis
- Production risk: GraphQL without complexity analysis crashes databases — always set depth and cost limits
- Biggest mistake: choosing SOAP for a public mobile API — heavy XML payload, slow onboarding, poor developer experience
Imagine ordering food. SOAP is like calling a restaurant on a landline — there's a strict script you must follow, and the restaurant confirms every detail back to you in writing. REST is like ordering via a website menu — you pick what you want from a fixed set of pages, and it just works. GraphQL is like texting a personal chef — you describe exactly what you want on your plate, no more, no less, and they deliver precisely that. Same goal (getting food), wildly different experiences.
Every application that talks to another application — a mobile app hitting a backend, a payment gateway, a dashboard pulling live data — uses an API. The style of that API determines how fast you can build, how well it scales, and how painful it is to change later. REST, SOAP, and GraphQL are the three dominant API styles in the industry, and choosing the wrong one is a silent tax that compounds over years.
The problem is that most tutorials describe these three as if they're interchangeable tools with different syntax. They're not. Each one was invented to solve a different pain point. SOAP was built when web services needed enterprise-grade reliability and formal contracts. REST emerged to make the web itself the platform — stateless, cacheable, universally accessible. GraphQL was created by Facebook because REST's rigid endpoint model broke down at the scale of a billion-user social graph.
By the end of this article you'll be able to explain the architectural philosophy behind each style, write and call real API examples in all three, articulate the performance and maintainability trade-offs in a team discussion, and — most importantly — confidently answer 'which should we use?' on your next project without Googling it.
What is REST vs SOAP vs GraphQL?
REST vs SOAP vs GraphQL is a core concept in CS Fundamentals. Rather than starting with a dry definition, let's see it in action and understand why it exists.
Each style emerged from a specific gap. SOAP (2000) gave enterprises a formal contract with WSDL and WS-Security — but wrapped every message in heavy XML. REST (2000, by Roy Fielding) turned HTTP into a stateless resource system, caching everything by default. GraphQL (2015, by Facebook) solved the problem of over- and under-fetching by letting clients define exactly what they need. The differences aren't just syntax — they reflect fundamentally different trade-offs in coupling, performance, and operational complexity.
Production teams often miss that REST isn't a protocol — it's a set of constraints. Most 'REST APIs' are just HTTP CRUD. That's fine if you know the trade-off: you lose hypermedia-driven discoverability. SOAP forces a contract, but that contract becomes a coordination bottleneck. GraphQL gives clients power but shifts complexity to resolvers. Don't choose based on hype — choose based on your data shapes and client diversity.
The real mistake is thinking REST is just 'CRUD over HTTP' without understanding the uniform interface constraint. If you treat it as just a style for mapping resources to URLs, you miss the caching and statelessness benefits that make it scale.
REST: Principles and Production Reality
REST (Representational State Transfer) is not a standard but an architectural style defined by Roy Fielding in 2000. It relies on stateless client-server communication, caching, and a uniform interface. In practice, most 'REST APIs' are just HTTP CRUD endpoints. True REST requires hypermedia (HATEOAS), which almost no one implements. That's okay — as long as you understand the trade-off.
Production REST is about resource modeling. Each endpoint represents a noun (e.g., /users, /orders), and HTTP methods map to actions. The real power is cacheability — HTTP caches at proxies, CDNs, and browsers can significantly reduce server load.
Where REST breaks down is when the client needs custom data shapes. You end up with either over-fetching (getting too much data) or under-fetching (needing multiple requests). That's where GraphQL steps in.
REST's big trap: versioning. Without an explicit versioning strategy, changing a response field forces all mobile clients to update. Always put a version in the URL or accept header, and document deprecation timelines.
Another trap: assuming caching works out of the box. You must explicitly set Cache-Control and ETag headers. Many teams forget, then wonder why their CDN serves stale data. Test caching behavior in staging with curl -I and verify headers.
If you're building a dynamic dashboard with REST, you'll feel the over-fetching pain immediately. The desktop browser might render fine, but your mobile app on a 3G connection will suffer. That's when you consider GraphQL for that specific use case.
SOAP: When You Need a Contract
SOAP (Simple Object Access Protocol) uses XML envelopes and a WSDL (Web Services Description Language) to define every message. It's heavy — XML parsing adds latency and payload size bloat. But it comes with built-in error handling (SOAP Faults), security (WS-Security), and transaction support.
In production, SOAP is used where compliance and formal contracts are non-negotiable: banking, insurance, government systems. The WSDL acts as a legal agreement — both sides know exactly what to send and receive.
SOAP's downside is flexibility. Changing a WSDL requires coordinated deployments across all consumers. It's also slow — a simple request can be 10KB in XML overhead. You don't use SOAP for a mobile app.
If you're forced to expose a SOAP service to modern clients, consider a protocol translation gateway that converts SOAP to REST or GraphQL on the fly. That buys you time while the legacy contract remains intact.
Performance tip: XML parsing is CPU-bound. At scale, offload parsing to a separate gateway or use a binary XML format like Fast Infoset if the other end supports it.
One more thing: never assume WSDL is self-documenting for external teams. Always provide a human-readable contract alongside the WSDL.
GraphQL: Flexibility vs Complexity
GraphQL, created by Facebook in 2015, lets clients specify exactly the data they need. A single endpoint accepts queries, mutations, and subscriptions. This eliminates over-fetching and under-fetching. But it shifts complexity to the server.
The server must resolve queries that can be arbitrarily deep and wide. Without proper guards, a client can craft a query that does exponentially more work than intended. This is the N+1 problem on steroids — each level of nesting can multiply database calls.
- Query complexity analysis (cost per field)
- Depth limiting
- DataLoader for batching
- Persisted queries for untrusted clients
GraphQL shines for dashboard applications and mobile apps where network round-trips are expensive. But it adds operational overhead — you need a schema registry, monitoring per field, and resolver profiling.
One pattern that works well: expose a GraphQL endpoint for read-heavy dashboards, but keep REST for simple CRUD mutations. Don't force everything through one style.
And here's a tip: persist queries for untrusted clients. It closes the door on ad-hoc query attacks entirely.
posts: [Post] field in the User type? Without DataLoader, fetching 100 users triggers 100 additional queries for posts. Always batch relation resolvers.Choosing an API Style: A Decision Framework
Stop choosing an API style based on hype or what your friend is using. The decision should come from your constraints:
- Client diversity: If you have mobile, web, and third-party clients, GraphQL reduces the number of endpoints and lets each client fetch exactly what it needs. But you pay in server complexity.
- Caching requirements: REST is trivial to cache at every layer (CDN, browser, server). GraphQL responses are harder to cache because queries vary. If your data changes rarely and you need fast reads, REST wins.
- Contract strictness: If you have multiple teams or external partners that must adhere to a fixed contract, SOAP's WSDL is actually a feature, not a bug. But you'll move slowly.
- Performance budget: SOAP adds ~200% overhead per request due to XML and envelope wrapping. REST with JSON adds ~30%. GraphQL can be as lean as 1 query per screen, but resolver performance varies.
Use this decision tree:
The rule of thumb: if you can't clearly articulate why you chose one style over another, you probably chose wrong. Take the time to map your constraints — it's cheaper than a migration later.
- REST: low cost to read, high cost to change response shape.
- SOAP: high cost to write client, low ambiguity in contract.
- GraphQL: low cost to query, high cost to secure and optimise.
Migrating Between Styles and Coexistence Patterns
In the real world, you'll rarely start from scratch. Most teams inherit a system built in one style and need to introduce another. The key is to isolate the styles at the API gateway, not within the same codebase.
A common pattern is the strangler fig: expose a new GraphQL endpoint alongside your existing REST API, and gradually migrate clients. The gateway routes requests based on either path prefix (e.g., /graphql vs /api/v1) or content negotiation headers.
Another pattern: use a protocol bridge. If your SOAP backend is rock-solid, wrap it with a RESTful proxy that translates JSON requests into SOAP envelopes. This gives you time to rewrite if needed, without forcing clients to change.
Performance tip: when running multiple styles, monitor the gateway's resource usage. XML parsing for SOAP can saturate CPU faster than JSON or GraphQL resolvers.
Authentication and rate limiting should be centralised at the gateway, not duplicated per style. Use a single OAuth 2.0 flow that applies to all routes.
One thing most teams forget: when you migrate, you'll likely have two versions of the same data source. Ensure consistency through a single source of truth or reconciliation jobs.
API Security: Common Pitfalls Across Styles
Security isn't an afterthought — it's baked into the style choice. Each API style introduces unique attack surfaces.
REST: The biggest risk is insecure direct object references (IDOR). A client requests /users/123 and gets another user's data if authorization is missing. Also, lack of rate limiting can lead to brute force. REST's statelessness forces you to validate every request.
SOAP: WS-Security is powerful but misconfigured more often than not. Replay attacks, XML injection, and oversized payloads are common. The WSDL can leak internal service details if exposed publicly.
GraphQL: Introspection exposes the full schema — an attacker can map all types and fields. Without depth and cost analysis, any authenticated client can craft a denial-of-service query. Field-level authorization is tricky because the same resolver may return data for users with different permission levels.
Production rule: never expose GraphQL introspection in production unless behind a VPN. Always implement field-level authorization guards in resolvers, not just in the schema.
A common production mistake: allowing introspection in production behind a VPN, thinking it's safe. It's not. One leak of a VPN credential and the attacker has your entire schema map.
Testing Strategies for REST, SOAP, and GraphQL
Each API style demands different testing approaches. You can't test a GraphQL resolver the same way you test a REST controller.
REST: Unit test controllers with mocked services. Integration test by starting an embedded server and hitting endpoints. Use contract tests with Spring Cloud Contract or Pact to ensure client and server agree on request/response shapes. Pay attention to status codes and error bodies.
SOAP: SoapUI or Postman with WSDL import. Validate XML schemas and SOAP faults. Test with different namespace combinations — mismatches cause hard-to-debug failures. Use performance tests with large payloads to catch XML parsing bottlenecks.
GraphQL: Test resolvers in isolation with mock data loaders. Write integration tests that execute queries against the full schema. Use persisted queries for critical flows to ensure queries don't change unexpectedly. Test both positive cases and malicious queries (complexity attacks, null injection).
Static analysis: Use tools like Spectral (REST), SOAPSonar (SOAP), and GraphQL Inspector to enforce style-specific rules in CI.
Don't forget to test the gateway if you use multiple styles. A wrong route config can silently serve stale data or leak internal endpoints.
And here's a counterintuitive tip: when testing REST, test for missing headers, not just wrong responses. Missing Cache-Control can cause a CDN cache stampede.
API Performance: Measuring Latency, Throughput, and Cost
Choosing an API style has direct performance implications that show up in production metrics. Latency, throughput, and operational cost vary significantly.
Latency: REST with JSON typically adds ~30% overhead over raw data due to HTTP framing and serialisation. SOAP adds ~200% due to XML envelope and namespace parsing. GraphQL can be faster per request (less data transferred) but resolver execution can add latency if not optimised with batching.
Throughput: REST handles high concurrent traffic well because of statelessness and caching. A well-cached REST endpoint can serve thousands of requests per second on a single instance. SOAP struggles — XML parsing is CPU-bound; at 500 req/s you'll likely need to scale horizontally. GraphQL throughput depends on query complexity; a simple query can match REST, but a deep nested query can tank throughput by orders of magnitude.
Cost: REST is cheapest to operate — simple infrastructure, caching reduces load. SOAP costs more per request due to CPU and bandwidth. GraphQL introduces operational complexity — you need resolver profiling, schema governance, and potentially a gateway.
Real-world benchmark: In a typical e-commerce API, a REST product endpoint returns ~1.2KB of JSON. GraphQL can reduce that to 300 bytes (75% reduction). But the resolver for that GraphQL query might need to fetch from 3 microservices — without DataLoader, that's 3 sequential HTTP calls, adding 150ms latency. With batching, it's one aggregation call, 50ms.
Always profile with your actual data shapes before committing to a style. What works for one team might be disastrous for another.
GraphQL Query Explodes Database Connection Pool
- Always enforce query complexity and depth limits on public GraphQL endpoints.
- Use DataLoader or similar batching to prevent N+1 queries in GraphQL resolvers.
- Monitor database connection usage per query — not just per endpoint.
Key takeaways
Common mistakes to avoid
6 patternsMemorising syntax before understanding the concept
Skipping practice and only reading theory
Choosing GraphQL for a simple CRUD app with one client
Using SOAP for public mobile APIs
Exposing GraphQL introspection in production
Assuming REST caching works out of the box without headers
Interview Questions on This Topic
What are the constraints of REST, and when would you violate them in production?
Frequently Asked Questions
That's Computer Networks. Mark it forged?
10 min read · try the examples if you haven't