← Back to blog

REST vs GraphQL: A Practical Comparison for Choosing Your API

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/42 is the user with ID 42. /products is the collection of all products.
  • HTTP methods: GET reads data, POST creates data, PUT/PATCH updates data, DELETE removes 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: 200 for success, 404 for not found, 401 for unauthorized, 500 for 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

CategoryRESTGraphQL
API DocumentationSwagger/OpenAPI (mature, widely adopted)GraphiQL, GraphQL Playground (self-documenting via schema)
API TestingPostman, Insomnia, curlPostman (GraphQL support), Insomnia, Apollo Studio
Client LibrariesAxios, Fetch API, any HTTP clientApollo Client, urql, Relay (React-specific)
Server FrameworksExpress, FastAPI, Spring Boot, LaravelApollo Server, Mercurius, GraphQL Yoga, Hasura
Code GenerationOpenAPI GeneratorGraphQL Code Generator (TypeScript types from schema)
MonitoringStandard APM toolsApollo 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

ScenarioRecommendation
Simple CRUD applicationREST
Public API for third-party developersREST
Content-heavy website with CDNREST or static generation
Complex data with deep relationshipsGraphQL
Multiple frontend clients (web + mobile)GraphQL
Rapid frontend iterationGraphQL
Real-time subscriptionsGraphQL (with subscriptions)
Microservice aggregation layerGraphQL as BFF
File uploadsREST
WebhooksREST

Want to know if your site is secure?

Request a free security audit. In 48 hours you get a complete report.

Request Free Audit

Quick Contact