Two Approaches to the Same Problem
REST and GraphQL both solve the same fundamental problem: how does a client (a web browser, a mobile app, another service) request data from a server? But they solve it in very different ways, and choosing between them has real consequences for your application's performance, developer experience, and long-term maintainability.
REST has been the dominant API paradigm since the mid-2000s. GraphQL was developed by Facebook in 2012 and open-sourced in 2015. Both are widely used in production today, and neither is going away. The question is not which one is "better" in the abstract, but which one fits your specific situation.
This article compares them honestly, with concrete examples and a decision framework at the end.
How REST Works
REST (Representational State Transfer) organizes your API around resources. Each resource (a user, a product, an order) has a URL, and you interact with it using standard HTTP methods.
Core Concepts
- Resources: Everything is a resource with a unique URL.
/users/42is the user with ID 42./productsis the collection of all products. - HTTP methods:
GETreads data,POSTcreates data,PUT/PATCHupdates data,DELETEremoves data. - Stateless: Each request contains all the information the server needs to process it. The server does not remember previous requests.
- Standard HTTP status codes:
200for success,404for not found,401for unauthorized,500for server error.
Example: Fetching a User Profile
Request:
GET /api/users/42
Response (200 OK):
{"id": 42, "name": "Maria Rossi", "email": "maria@example.com", "department": "Engineering", "role": "Lead Developer", "joined": "2019-03-15", "avatar_url": "/avatars/42.jpg", "phone": "+41 91 123 4567", "address": {"street": "Via Nassa 12", "city": "Lugano", "zip": "6900", "country": "CH"}, "preferences": {"language": "it", "theme": "dark", "notifications": true}}
The server returns the complete user resource. The client gets everything the server decides to include, whether the client needs all of it or not.
Example: Fetching a User's Orders
To get the same user's orders, you make a second request:
Request:
GET /api/users/42/orders
Response (200 OK):
[{"id": 101, "date": "2022-04-10", "total": 250.00, "status": "shipped", "items": [...]}, {"id": 98, "date": "2022-04-01", "total": 89.90, "status": "delivered", "items": [...]}]
Two requests to display one page that shows a user profile with their recent orders.
How GraphQL Works
GraphQL takes a fundamentally different approach. Instead of multiple endpoints returning fixed data structures, there is a single endpoint. The client specifies exactly what data it needs in a query, and the server returns precisely that data and nothing more.
Core Concepts
- Single endpoint: All requests go to one URL (typically
/graphql). - Client-driven queries: The client defines the shape and content of the response.
- Typed schema: The API has a schema that defines all available types, fields, and relationships. The schema is self-documenting and can be explored by clients.
- Nested data: Related data can be fetched in a single query by traversing relationships.
Example: Fetching the Same User Profile with Orders
Request:
POST /graphql
{"query": "{ user(id: 42) { name email department orders(last: 5) { id date total status } } }"}
Response (200 OK):
{"data": {"user": {"name": "Maria Rossi", "email": "maria@example.com", "department": "Engineering", "orders": [{"id": 101, "date": "2022-04-10", "total": 250.00, "status": "shipped"}, {"id": 98, "date": "2022-04-01", "total": 89.90, "status": "delivered"}]}}}
One request. The response contains exactly the fields the client asked for. No phone number, no address, no preferences, no avatar URL. Just the name, email, department, and last 5 orders with specific fields.
The Over-Fetching and Under-Fetching Problem
This is the core issue that motivated GraphQL's creation. With REST, you frequently encounter two problems:
Over-Fetching
The server returns more data than the client needs. In our REST example, to display a user's name and email on a dashboard, you still get their phone number, address, preferences, and avatar URL. On a fast connection, the extra bytes are negligible. On a mobile connection, transmitting unnecessary data wastes bandwidth and slows down the experience.
Over-fetching gets worse as APIs mature. Fields are added to resources over time to serve different clients' needs, and each client ends up receiving data intended for other clients.
Under-Fetching
The opposite problem: the server does not return enough data in a single response, forcing the client to make multiple sequential requests. In our REST example, getting user data and orders required two requests. A real-world page might need five or six separate API calls to render completely.
Under-fetching leads to waterfall requests: the client makes request A, waits for the response, then makes request B (which depends on data from A), waits for that response, then makes request C. Each round trip adds latency.
GraphQL solves both problems. The client asks for exactly what it needs (no over-fetching) and gets it all in one request (no under-fetching).
When to Use REST
REST is not obsolete. For many use cases, it is the better choice. Here are the situations where REST shines:
Simple CRUD Operations
If your API is primarily about creating, reading, updating, and deleting resources with simple data structures, REST's resource-oriented model maps naturally to your domain. A blog API with posts, comments, and authors. An inventory system with products, categories, and suppliers. These are straightforward REST territory.
Public APIs
If you are building an API that external developers will consume, REST is generally easier to understand and use. The learning curve is lower, documentation tools (Swagger/OpenAPI) are mature, and most developers have REST experience. Public GraphQL APIs exist (GitHub's, for example), but they require more upfront investment from API consumers.
Caching
REST's resource-based URLs work naturally with HTTP caching. GET /api/products/42 can be cached at every level: browser cache, CDN cache, reverse proxy cache. The same URL always returns the same resource (unless the resource has changed), making cache invalidation straightforward.
GraphQL uses POST requests to a single endpoint, which makes HTTP-level caching harder. You can still cache at the application level, but it requires more effort and custom infrastructure.
File Uploads and Downloads
REST handles binary data naturally through multipart form data. GraphQL requires workarounds for file handling (like base64 encoding or separate upload endpoints), which add complexity.
Microservice Communication
For service-to-service communication where each service has well-defined data needs, REST's simplicity is an advantage. The overhead of GraphQL's query parsing and validation adds latency that may not be justified when the client is a server that always needs the same data.
When to Use GraphQL
GraphQL excels in specific situations that play to its strengths:
Complex Data Relationships
When your data model has deep relationships (users who have orders that contain products that belong to categories that have related products), GraphQL's nested query structure lets clients traverse these relationships efficiently. With REST, this would require many sequential requests or custom endpoints that bundle data in specific ways.
Multiple Frontend Clients
If you have a web app, a mobile app, and maybe a partner integration all consuming the same API, GraphQL's client-driven queries mean each client can request exactly the data it needs. The mobile app (which needs to be bandwidth-efficient) asks for fewer fields than the web app. No need for different API versions or client-specific endpoints.
Rapid Frontend Iteration
When your frontend changes frequently (new features, layout experiments, A/B tests), GraphQL lets frontend developers modify their data requirements without backend changes. Need a new field on the dashboard? Add it to the query. Removed a component that showed user preferences? Remove those fields from the query. No backend deployment needed.
Mobile Applications
Mobile connections are often slower and more expensive than desktop. GraphQL's ability to fetch exactly what is needed in a single request reduces both latency and data consumption, which translates into better battery life and lower data costs for users.
Aggregating Data from Multiple Sources
GraphQL can serve as a unified API layer that aggregates data from multiple backend services, databases, or third-party APIs. A single GraphQL query can fetch user data from one service, order data from another, and inventory data from a third, presenting it all as a unified response to the client.
Performance Considerations
Performance is not simply "REST is faster" or "GraphQL is faster." Both can be fast or slow depending on implementation.
Network Performance
GraphQL typically wins on network performance because of reduced round trips and smaller payloads. One request instead of five, with only the data the client needs. This advantage is most pronounced on high-latency connections (mobile networks, users far from the server).
Server Performance
GraphQL queries can be computationally expensive on the server side. A client can construct a query that traverses deep relationships and requests large amounts of data. Without safeguards, this can overload the server. REST endpoints, by contrast, have predictable resource consumption because the server controls what data is returned.
Caching
REST has a clear advantage in HTTP-level caching. CDNs, browser caches, and reverse proxies all work natively with REST's URL-based resource model. GraphQL requires application-level caching solutions (like DataLoader for batching database queries, or Apollo's normalized cache on the client side).
For public-facing websites where caching is critical for performance and cost (like the kind we build at Envestis), REST or even pre-rendered static pages served from a CDN will outperform GraphQL in most scenarios. See our article on website speed and business impact for why loading performance matters commercially.
Security Implications
Both REST and GraphQL have security considerations, but they differ in nature.
Rate Limiting
REST APIs are straightforward to rate limit: you count requests per endpoint per time period. /api/users gets 100 requests per minute, /api/products gets 200.
GraphQL rate limiting is more complex because one request can do the work of many REST requests. You need to consider query complexity, not just request count. A simple query that fetches one user's name should not count the same as a query that fetches 1,000 users with all their orders and order items.
Query Depth and Complexity Attacks
GraphQL introduces a risk that REST does not have: malicious queries. A client can construct a deeply nested query that forces the server to execute millions of database queries:
{ users { orders { items { product { reviews { author { orders { items { product { ... } } } } } } } } } }
Without query depth limits and complexity analysis, this kind of query can be used for denial-of-service attacks. Every GraphQL API needs safeguards: maximum query depth, complexity scoring, and timeouts.
Authorization
REST authorization is typically per-endpoint: "this user can access /api/admin/users." GraphQL authorization needs to be per-field or per-type: "this user can see user.email but not user.salary." This is more granular but also more complex to implement and audit.
Error Exposure
GraphQL returns errors inline with data, which can inadvertently expose internal information. REST APIs return standard HTTP error codes that reveal less about internal structure. Both require careful error handling, but GraphQL needs more attention to ensure error messages do not leak sensitive details.
Introspection
GraphQL's introspection feature lets clients query the schema itself, discovering all available types, fields, and relationships. This is useful during development but should be disabled in production for public-facing APIs, as it exposes your entire data model to potential attackers.
Tooling Comparison
| Category | REST | GraphQL |
|---|---|---|
| API Documentation | Swagger/OpenAPI (mature, widely adopted) | GraphiQL, GraphQL Playground (self-documenting via schema) |
| API Testing | Postman, Insomnia, curl | Postman (GraphQL support), Insomnia, Apollo Studio |
| Client Libraries | Axios, Fetch API, any HTTP client | Apollo Client, urql, Relay (React-specific) |
| Server Frameworks | Express, FastAPI, Spring Boot, Laravel | Apollo Server, Mercurius, GraphQL Yoga, Hasura |
| Code Generation | OpenAPI Generator | GraphQL Code Generator (TypeScript types from schema) |
| Monitoring | Standard APM tools | Apollo Studio, Stellate (formerly GraphCDN) |
REST's tooling ecosystem is larger and more mature because REST has been around longer. GraphQL's tooling is catching up rapidly and has some unique advantages, particularly around type safety and code generation. GraphQL Code Generator can automatically produce TypeScript types from your schema, which eliminates an entire category of bugs.
A Decision Framework
Here is a practical framework for choosing between REST and GraphQL. Answer these questions about your project:
1. How complex is your data model?
Simple (flat resources, few relationships): REST. The overhead of GraphQL is not justified when your data model is straightforward. A blog with posts and comments, a product catalog, a contact form backend: REST handles these well.
Complex (deep relationships, interconnected data): GraphQL. When displaying a page requires data from multiple related entities, GraphQL's nested queries reduce complexity on the client side.
2. How many client types do you serve?
One or two clients with similar needs: REST. When all clients need roughly the same data, the flexibility of GraphQL is not needed.
Multiple clients with different data needs: GraphQL. A web app, mobile app, and smartwatch app all consuming the same API but needing different subsets of data is exactly what GraphQL was designed for.
3. How important is caching?
HTTP caching is a priority (public websites, CDN usage): REST. Native HTTP caching is a significant performance advantage for content-heavy sites.
Application-level caching is acceptable: Either. If you are already using a caching layer like Redis, the HTTP caching advantage of REST is less relevant.
4. What is your team's experience?
Team knows REST well, new to GraphQL: Consider the learning curve. GraphQL requires understanding schemas, resolvers, query optimization, and new security patterns. The transition has real costs in time and mistakes.
Team has GraphQL experience: Use GraphQL where it makes sense. Do not use REST just because it is the default.
5. Is this a public or internal API?
Public API for external developers: REST, usually. Lower barrier to entry, better documentation tooling, and most developers know how to consume a REST API without learning new concepts.
Internal API for your own frontends: Either, based on the data complexity and client variety factors above.
The Hybrid Approach
You do not have to choose one exclusively. Many production systems use both:
- REST for simple CRUD and public APIs where caching and simplicity matter.
- GraphQL as a BFF (Backend for Frontend) that aggregates data from REST microservices and provides a flexible query interface to frontend clients.
- REST for file uploads and webhooks, GraphQL for data queries.
GitHub, for example, maintains both a REST API (v3) and a GraphQL API (v4). They recommend GraphQL for complex data fetching and REST for simple operations.
The important thing is to make a conscious decision based on your requirements, not default to one because it is trendy or because it is what you have always used. If you are planning an API architecture for a new project and want a second opinion, our development team in Lugano is happy to review your requirements and recommend the right approach.
Summary: When to Use What
| Scenario | Recommendation |
|---|---|
| Simple CRUD application | REST |
| Public API for third-party developers | REST |
| Content-heavy website with CDN | REST or static generation |
| Complex data with deep relationships | GraphQL |
| Multiple frontend clients (web + mobile) | GraphQL |
| Rapid frontend iteration | GraphQL |
| Real-time subscriptions | GraphQL (with subscriptions) |
| Microservice aggregation layer | GraphQL as BFF |
| File uploads | REST |
| Webhooks | REST |
Want to know if your site is secure?
Request a free security audit. In 48 hours you get a complete report.
Request Free Audit