· NERVICO · software-development · 10 min read
REST vs GraphQL: When to Use Each
Technical comparison between REST APIs and GraphQL: real differences, advantages and disadvantages of each approach, and practical criteria to decide which to use in your project.
GitHub migrated its public API from REST to GraphQL in 2017. Shopify did the same with its storefront API. But Amazon, Google Cloud, and most infrastructure services continue to use REST exclusively. Netflix abandoned Falcor (their internal alternative to GraphQL) and now uses a combination of REST and GraphQL depending on the use case.
These facts dismantle the simplistic narrative that GraphQL is “the successor to REST.” It is not. They are different tools that solve different problems. The right question is not “which is better” but “which fits better in my context.”
In this guide you will find an honest technical comparison, with real trade-offs, and a decision framework you can apply to your next project.
What REST and GraphQL Actually Are
REST: The De Facto Standard
REST (Representational State Transfer) is an architectural style for designing APIs. It is not a protocol or a formal specification. It is a set of principles:
Resources: Everything is modeled as resources identified by URLs. A user is /users/123, an order is /orders/456.
HTTP methods: Operations map to standard HTTP methods: GET for reading, POST for creating, PUT/PATCH for updating, DELETE for deleting.
Stateless: Each request contains all the information needed to be processed. The server maintains no state between requests.
Structured responses: Typically JSON, with HTTP status codes indicating the result (200 OK, 404 Not Found, 500 Internal Server Error).
A typical REST endpoint:
GET /api/users/123Returns all fields for user 123: name, email, address, order history, preferences… Everything. Always.
GraphQL: Queries on Demand
GraphQL is a query language for APIs created by Facebook in 2012 and released as open source in 2015. Its fundamental principle is that the client defines exactly what data it needs.
Typed schema: The API defines a schema with types, fields, and relationships. The client knows exactly what it can request.
Single endpoint: Instead of multiple URLs, everything goes through a single endpoint (typically /graphql).
Flexible queries: The client specifies exactly which fields it needs in each request.
A typical GraphQL query:
query {
user(id: "123") {
name
email
orders(last: 5) {
id
total
}
}
}Returns only name, email, and the last 5 orders for the user. Nothing more, nothing less.
The Real Differences That Matter
Over-Fetching and Under-Fetching
The problem with REST: When you request a resource, you get all its fields. If you only need a user’s name and email, you still receive the address, preferences, history, and everything else. This is over-fetching.
The opposite also occurs. If you need data about a user and their orders, you need two requests: one to /users/123 and another to /users/123/orders. This is under-fetching, and it creates the N+1 problem: to show a list of 20 users with their orders, you need 21 HTTP requests.
GraphQL’s solution: The client requests exactly what it needs. One field, twenty fields, or data from multiple related entities, all in a single request.
But there are nuances:
REST can mitigate over-fetching with sparse fields (for example, ?fields=name,email). OpenAPI supports this functionality. It is not as elegant as GraphQL, but it works.
REST can mitigate under-fetching with includes (for example, ?include=orders). It is a partial solution that adds complexity to the endpoint.
GraphQL introduces the risk of under-querying: the client might request too little data and need additional queries. It also introduces the risk of over-querying: queries requesting deeply nested data can be more expensive than the REST equivalent.
Performance and Caching
REST has an advantage in caching. URLs are perfect unique identifiers for HTTP caches. CDNs, proxies, and browsers cache REST responses natively. If you request GET /api/products/123, the response can be cached at all levels.
GraphQL complicates caching. Since all requests go to the same endpoint (POST /graphql), standard HTTP caches do not work. You need application-level caching (Apollo Client, urql) or field-resolution-level caching.
Real data:
- A well-cached REST API can respond in 5-15ms from CDN
- A typical GraphQL query takes 50-200ms from the server
- For public APIs with high read traffic, the caching difference is significant
- For internal APIs with frequently changing data, the difference is irrelevant
Backend Complexity
REST is simpler to implement. Each endpoint is a function that reads or writes data. Logic is localized and easy to understand.
GraphQL adds significant complexity:
- You need to define and maintain a typed schema
- Resolvers can generate performance problems if not optimized (N+1 problem at the resolution level)
- You need additional tools like DataLoader for batch queries
- Authorization is more complex (you have to control access at the field level, not just the endpoint level)
- Monitoring and debugging is more difficult (all requests are POST to the same endpoint)
Revealing data: According to the State of JavaScript 2024 survey, 42% of developers using GraphQL report that server complexity is the biggest challenge. Only 18% report problems with REST over-fetching.
Frontend Experience
GraphQL significantly improves the frontend experience:
- Frontend can request exactly the data it needs without waiting for backend changes
- Automatically generated types (TypeScript codegen) eliminate integration errors
- Tools like Apollo DevTools facilitate debugging
- Fragments allow reusing query patterns
REST requires more coordination:
- Frontend depends on backend endpoint structure
- Changes in needed data require backend changes (new endpoint or modifying an existing one)
- Types must be defined manually or generated from OpenAPI
But note: If your backend and frontend team are the same people (common in small projects), GraphQL’s advantage diminishes. Coordination is not a problem when you are coordinating with yourself.
API Evolution
REST handles evolution through versioning. /api/v1/users and /api/v2/users can coexist. It is simple but can lead to maintaining multiple versions.
GraphQL evolves without versioning. You can add new fields without breaking existing clients. You can deprecate fields and remove them gradually. You do not need versions because the client only requests what it uses.
GraphQL’s advantage here is real, especially for public APIs with many clients you cannot control. For internal APIs where you control all clients, REST versioning is sufficient.
When to Use REST
Public APIs With High Traffic
If you are building an API that will be consumed by many external clients and read traffic is high, REST with aggressive caching is hard to beat.
Examples: Data APIs (weather, financial, geographic), content APIs (headless CMS), infrastructure APIs.
Teams With REST Experience
If your team knows REST well and has no GraphQL experience, GraphQL’s learning curve (server-side especially) can delay the project by 2-4 weeks. For projects with tight deadlines, this is a real risk.
Simple CRUD Operations
If your API is basically CRUD (create, read, update, delete) without complex relationships between entities, REST is more straightforward. You do not need GraphQL flexibility if your queries are predictable.
Microservices and Service-to-Service Communication
Service-to-service communication is different from client-server communication. Services know exactly what data they need from other services. GraphQL adds no advantage here, and adds latency through the resolution layer.
For inter-service communication: REST, gRPC, or asynchronous messaging.
When to Use GraphQL
Applications With Complex UIs
Applications like dashboards, data platforms, or rich interfaces that need to combine data from multiple sources in a single view. GraphQL lets the frontend get exactly the data it needs without orchestrating multiple REST calls.
APIs Consumed by Multiple Clients
If your API is consumed by a web app, a mobile app, and perhaps third parties, each client needs different data. With REST, you end up with custom endpoints per client or generalized over-fetching. GraphQL solves this naturally.
Teams Where Frontend and Backend Work Independently
GraphQL acts as a contract between teams. Frontend can advance with mock data while backend implements resolvers. The schema is the living documentation.
Rapid Prototyping With Schema-First
If you start with the schema before implementing, you can validate the data structure with stakeholders before writing a single line of backend code. Tools like Apollo Sandbox let you explore the schema and test queries.
The Hybrid Approach: REST and GraphQL Together
Backend for Frontend (BFF) With GraphQL
An increasingly common pattern: use REST for communication between backend services and GraphQL as an aggregation layer for the frontend.
How it works:
- Microservices expose internal REST APIs
- A GraphQL service acts as a gateway that aggregates data from multiple REST services
- The frontend consumes the GraphQL API
Advantages:
- Each layer uses the most appropriate tool
- Microservices maintain REST simplicity
- Frontend gets GraphQL flexibility
- Caching works at the REST service level
Disadvantages:
- An additional infrastructure layer
- More operational complexity
- You need a team with experience in both technologies
Federation: Distributed GraphQL
Apollo Federation allows multiple services to contribute to a single GraphQL schema. Each service defines its part of the schema, and a gateway combines them.
When to consider Federation:
- Multiple teams working on different domains
- Complex schema that would be unmanageable in a single service
- Need to maintain team autonomy
When to avoid it:
- Small teams (fewer than 3 backend teams)
- Simple schemas
- When Federation complexity outweighs the benefit
Performance: Real Data
Typical Benchmarks
These figures are indicative and depend enormously on implementation:
| Metric | REST (optimized) | GraphQL (optimized) |
|---|---|---|
| Simple latency (1 resource) | 10-30ms | 20-50ms |
| Complex latency (nested data) | 100-300ms (multiple requests) | 50-150ms (one request) |
| CDN caching | Native, very efficient | Requires special configuration |
| Response size (partial data) | Larger (over-fetching) | Smaller (only what is requested) |
| Bandwidth usage | Higher | Lower |
Performance Considerations
For REST:
- Implement aggressive HTTP caching (Cache-Control, ETag)
- Use sparse fields when possible
- Consider cursor-based pagination instead of offset
- Compress responses with gzip/brotli
For GraphQL:
- Implement DataLoader to avoid N+1 problems
- Limit query depth to prevent abuse
- Use persisted queries to improve caching
- Implement cost analysis to control expensive queries
Decision Framework
Key Questions
Answer these questions for your project:
- Do you have multiple clients with different needs? If yes, GraphQL has an advantage.
- Is your API mostly simple CRUD? If yes, REST is more direct.
- Is HTTP caching critical for your performance? If yes, REST is simpler.
- Does the frontend need to combine data from multiple sources? If yes, GraphQL reduces frontend complexity.
- Does your team have GraphQL experience? If no, add 2-4 weeks of learning curve.
- Is it a public or internal API? Public APIs with many consumers benefit more from GraphQL.
Quick Decision Table
| Scenario | Recommendation |
|---|---|
| Public API with high read traffic | REST |
| Internal CRUD API | REST |
| Dashboard with data from multiple sources | GraphQL |
| Mobile app + web + public API | GraphQL |
| Microservices communicating with each other | REST or gRPC |
| Quick prototype, team without GraphQL experience | REST |
| Platform with complex schema and multiple teams | GraphQL with Federation |
Common Mistakes When Choosing
Choosing GraphQL for Hype
“Everyone is migrating to GraphQL” is not a technical argument. Evaluate your context. If your 3-person team is building a CRUD API for a single web client, GraphQL adds complexity without proportional benefit.
Underestimating GraphQL Server Complexity
The frontend experience with GraphQL is excellent. The backend experience can be complex: schemas, resolvers, DataLoaders, field-level authorization, monitoring… All of this needs time and experience.
Not Considering the Hybrid Approach
REST vs GraphQL does not have to be a binary decision. Many successful projects use both: REST for simple operations and inter-service communication, GraphQL for complex frontend queries.
Ignoring Migration Cost
If you already have a working REST API, migrating to GraphQL has a significant cost. Do not migrate for trends. Migrate when over-fetching or under-fetching problems are causing real, measurable issues.
Conclusion
REST and GraphQL are tools, not identities. The best API is the one that solves your team’s and users’ problems with the least possible complexity.
Three principles for deciding:
- Start with REST. If you do not have clear reasons to use GraphQL, REST is simpler, better known, and easier to operate.
- Migrate to GraphQL when you have real problems. If over-fetching causes performance issues, if multiple clients need different data, or if frontend-backend coordination is a bottleneck, GraphQL solves them.
- Consider the hybrid approach. It does not have to be one or the other. Use each tool where it fits best.
If you need help designing the API architecture for your custom software projects, at NERVICO we have experience with both approaches and can help you choose the one that best fits your context.