Skip to content

REST vs GraphQL

1. Decision

Choose REST for simple, resource-oriented public APIs with predictable endpoint data structures, heavy CDN caching requirements, and straightforward security controls; choose GraphQL for complex client-driven applications (mobile/SPA dashboards) requiring flexible query selection, elimination of over-fetching/under-fetching, and aggregation across multiple microservice domains in a single network round-trip.


2. 30-Second Answer

REST (Representational State Transfer) exposes fixed, server-defined endpoint URLs (/users/123/orders) returning static JSON data structures. It excels at simplicity, CDN HTTP caching (Cache-Control), and granular security authorization per endpoint, but suffers from over-fetching (returning unnecessary fields) and under-fetching (requiring multiple HTTP round-trips to assemble a view). GraphQL is a client-driven query language and server-side runtime operating over a single POST endpoint (/graphql). Clients specify the exact fields, nested entities, and aliases required, consolidating multiple REST calls into a single network payload. However, GraphQL introduces severe server-side performance risks (the \(N+1\) query problem), complex query complexity validation, and difficult CDN caching.


3. Decision Matrix

Option Best For Avoid When Latency Scalability Consistency Operational Complexity
REST (HTTP / JSON) Standard CRUD APIs, CDN edge-cached public endpoints, file upload/download, simple client layouts Complex mobile views requiring data from 5+ endpoints, highly customized per-device payloads Low to Medium (Multiple round-trip latency overhead) High (Simple stateless HTTP caching) Standardized HTTP status codes & REST conventions Low
GraphQL Mobile apps, Single Page Apps (SPA), dashboard aggregators (BFF pattern), rapidly evolving UI views Simple CRUD apps, heavy file uploads, public APIs requiring simple CDN caching, write-heavy streaming Very Low (Single round-trip); High server CPU on un-optimized queries Medium (Requires DataLoader batching & query depth limits) Single HTTP 200 responses (Errors embedded in JSON payload) High (Schema stitching, DataLoader, complexity analysis)

4. Selection Rules

Requirement Best Choice Rationale / Example
Eliminate Over-Fetching & Under-Fetching GraphQL Mobile client requests only id and name, skipping 50 unneeded database fields
Public API with Heavy CDN Caching REST Standard HTTP GET URLs (/api/v1/products) cached seamlessly at CDN edge
Single Network Round-Trip Data Aggregation GraphQL Fetch User profile, Recent Orders, and Saved Items in 1 query payload
Simple CRUD Microservice APIs REST Simple endpoint URLs (GET, POST, PUT, DELETE) without schema compiler overhead
Strict Query Complexity & Rate Limiting REST Restrict rates by URL path; GraphQL requires calculating AST query complexity weights
Backend-For-Frontend (BFF) Pattern GraphQL Aggregates 10 backend microservices into a unified schema for iOS/Android apps

5. Tradeoffs

  1. Client Payload Customization vs Server CPU Query Cost
  2. Gain (GraphQL): Clients request exact JSON field shape, saving mobile bandwidth.
  3. Lose (GraphQL): Server must dynamically parse Abstract Syntax Trees (AST) and execute field resolvers.
  4. Single Endpoint Aggregation vs Endpoint-Level CDN Caching
  5. Gain (GraphQL): Fetch multi-domain entity graphs in 1 HTTP POST request.
  6. Lose (GraphQL): Standard HTTP GET CDN caching is broken because all requests hit /graphql via HTTP POST.
  7. Flexible UI Iteration vs The \(N+1\) Query Problem
  8. Gain (GraphQL): UI teams add/remove fields without waiting for backend API updates.
  9. Lose (GraphQL): Naive resolver execution triggers catastrophic \(N+1\) SQL query cascades against databases.
  10. Strong Schema Type System vs HTTP Status Code Standard
  11. Gain (GraphQL): Introspective GraphQL schema with strict type validation (GraphQL SDL).
  12. Lose (GraphQL): Always returns HTTP 200 OK, returning errors inside an errors: [] array payload.
  13. Security Rate Limiting Complexity
  14. Gain (REST): Simple rate-limiting by client IP and HTTP route endpoint.
  15. Lose (GraphQL): Malicious clients can send deeply nested recursive queries (user { friends { friends { friends } } }), crashing the server.

6. Interview Counter Questions

  1. What is the \(N+1\) Query Problem in GraphQL and how does DataLoader solve it using Batching and Caching?
  2. How do you implement Caching in GraphQL (Persisted Queries & Client Normalized Caches)?
  3. How do you prevent Denial of Service (DoS) attacks on a public GraphQL API (Query Depth & Complexity Analysis)?
  4. Why does GraphQL always return HTTP 200 OK even when execution errors occur?
  5. How does GraphQL Schema Stitching / Federation work in a distributed microservices architecture?
  6. How do file uploads work in REST vs GraphQL?
  7. What is GraphQL Subscriptions and how does it compare to WebSockets and Server-Sent Events (SSE)?
  8. How do you implement Field-Level Authorization in GraphQL resolvers?
  9. When would you choose REST over GraphQL for a mobile application backend?
  10. What is Automatic Persisted Queries (APQ) and how does it enable CDN caching for GraphQL?

7. Gold Standard Answers

  1. \(N+1\) occurs when fetching 1 parent entity triggers \(N\) separate DB queries for child relations. DataLoader solves this by batching all keys requested within a single event loop tick into a single SELECT * WHERE id IN (...) SQL query.
  2. Use Automatic Persisted Queries (APQ): Client sends SHA-256 hash of query via HTTP GET. CDN caches GET response by hash. On client side, use Normalized Caches (Apollo/Relay) keying items by __typename:id.
  3. Enforce Query Depth Limits (max 5-7 nested levels) and Query Complexity Analysis (assign point weights to fields, rejecting requests exceeding e.g. 1000 points) at the AST validation phase before resolver execution.
  4. GraphQL decouples network transport from execution payload. The HTTP request succeeded (200 OK), but specific field resolvers inside the execution graph failed, returning partial data alongside an errors: [] array.
  5. Apollo Federation defines a unified supergraph schema composed of subgraphs owned by independent microservices. An API Gateway (Router) parses incoming GraphQL queries and routes field fragments to respective microservices.
  6. REST handles multipart uploads natively via multipart/form-data. GraphQL handles file uploads via GraphQL Multipart Request Spec (complex base64 encoding), or better: returning an S3 Presigned URL via GraphQL mutation for direct client upload.
  7. GraphQL Subscriptions use WebSockets under the hood to stream real-time event updates for specific GraphQL selection sets when server-side data mutations occur.
  8. Enforce authorization inside domain service layers or middleware wrappers around individual field resolver functions, verifying user JWT claims against requested entity attributes before returning data.
  9. Choose REST for mobile backends when views map 1:1 to clear domain entities, HTTP GET CDN caching is critical, or team lacks experience managing GraphQL DataLoader and AST security complexity.
  10. APQ: Client sends short SHA-256 hash of query via HTTP GET. CDN caches response by hash. If CDN misses, client resends full GraphQL query via POST to register the hash on the server.

8. Common Mistakes

  • Deploying GraphQL resolvers without DataLoader, causing catastrophic \(N+1\) database query crashes under load.
  • Exposing an un-guarded public GraphQL endpoint without Query Depth Limiting or Complexity Analysis.
  • Handling authorization only at the GraphQL gateway layer instead of inside domain service layers.
  • Using GraphQL for simple binary file download/upload streaming endpoints instead of standard REST.
  • Expecting standard HTTP status codes (404, 500) for error handling in client applications instead of parsing GraphQL errors[].

9. Keywords

REST, GraphQL, Over-Fetching, Under-Fetching, N+1 Query Problem, DataLoader, Batching, AST, Query Depth Limiting, Query Complexity Analysis, Automatic Persisted Queries (APQ), Apollo Federation, Subscriptions, Normalized Cache.


10. Cheat Sheet

  • Core Difference: REST = Server-defined URLs & fixed payloads (HTTP GET, CDN friendly); GraphQL = Client-defined selection sets over single endpoint (/graphql).
  • Performance Rule: Always use DataLoader in GraphQL server resolvers to collapse \(N+1\) database queries into single IN (...) queries.
  • Security Rule: Enforce Query Depth Limits (max 5-7 levels) and Complexity Weight Analysis to block malicious recursive queries.
  • CDN Caching: Use Automatic Persisted Queries (APQ) to convert GraphQL queries into GET requests cached at the CDN edge.
  • Error Handling: GraphQL returns HTTP 200 OK with errors array inside JSON payload (errors: [{ message, path }]).
  • File Uploads: Don't stream files through GraphQL. Use a GraphQL mutation to return an S3 Presigned URL for direct client upload.

11. Related Decisions