Search interest in “REST vs GraphQL” has remained consistently high throughout the 2020s, with the debate gaining renewed urgency as more teams build frontend-heavy products with complex data requirements. GraphQL has been in production since Facebook open-sourced it in 2015 and is now mature, well-tooled, and genuinely adopted at scale. Yet REST remains the dominant choice for new APIs in 2026, and not without reason. The question is not which one is theoretically better but which one fits your project.

This guide covers what each approach actually is, the core technical differences with a concrete code example, performance realities that the marketing does not mention, security considerations that change the calculus, and a clear recommendation for each scenario type. By the end you will have a decision framework rather than another inconclusive comparison.

TL;DR

  • REST is the right default for most projects in 2026: simpler to implement, easier to document, better HTTP caching, and widely understood by external consumers
  • GraphQL justifies its complexity when you have a genuinely complex data graph, multiple clients with different data needs, or a frontend team that needs to iterate without backend changes
  • GraphQL’s N+1 query problem and caching challenges are real production costs that most comparisons understate; plan for DataLoader and persisted queries from day one
  • If you are building a public API for third-party developers, REST wins on discoverability and tooling availability; GraphQL shines for internal APIs where you control both ends

What REST Actually Is

REST (Representational State Transfer) is an architectural style, not a protocol. Roy Fielding defined it in his 2000 doctoral dissertation as a set of constraints for distributed hypermedia systems. In practice, a RESTful API means: stateless communication over HTTP, resource-oriented URLs, standard HTTP verbs (GET, POST, PUT, PATCH, DELETE) to indicate intent, and standard HTTP status codes to communicate outcomes.

A REST endpoint for a user resource looks like GET /api/v1/users/123. The server returns a representation of that resource. The client does not tell the server what fields it wants; the server decides what to include in the response. The API is versioned in the URL (/v1/, /v2/) when breaking changes require it.

What REST is not: a standard with a formal specification. Two REST APIs can behave very differently while both being technically “RESTful.” This is why OpenAPI (formerly Swagger) has become the de facto documentation and validation layer for REST APIs. It is not required, but a well-maintained OpenAPI spec is the closest thing REST has to a formal contract.

What GraphQL Actually Is

GraphQL is a query language for APIs and a runtime for executing those queries. The key distinction from REST is that the client specifies exactly what data it wants, not the server. A single GraphQL endpoint (typically POST /graphql) accepts queries written in the GraphQL query language and returns precisely the fields requested.

GraphQL requires a typed schema that describes every type in your data model and every query, mutation, and subscription available. This schema is the contract between client and server. Introspection allows clients to query the schema itself to discover what is available, which powers excellent developer tooling.

Developed by Facebook to solve the data-fetching challenges of their mobile app in 2012, it was open-sourced in 2015 and is now governed by the GraphQL Foundation. Major users include GitHub, Shopify, Twitter, and Airbnb. It is a mature, production-proven technology with a strong ecosystem.

The Core Differences Explained

Data Fetching

REST returns the entire resource representation defined by the server. If the /api/users/123 endpoint returns 20 fields and you only need 3, you receive all 20. GraphQL returns exactly the fields you request. Nothing more.

This matters most on mobile clients where bandwidth is constrained and payload size directly affects performance. It matters less for internal server-to-server communication where the full object is often useful.

Multiple Resources in One Request

REST typically requires one HTTP request per resource. To fetch a user and their associated orders and each order’s product details, you make three separate requests: one for the user, one for their orders, one for the products. Each is a round-trip.

GraphQL resolves related data in a single request. A single query can traverse the data graph and return deeply nested related entities without additional round-trips.

Over-fetching and Under-fetching

Over-fetching is receiving more data than you need. Under-fetching is receiving too little, requiring additional requests. REST APIs optimised for one client tend to over-fetch for another. Mobile apps often under-fetch from endpoints designed for web clients.

GraphQL eliminates both problems by design: the client specifies exactly what it needs.

The Type System

GraphQL has a mandatory strongly-typed schema. Every type, field, argument, and return value is declared. The schema is the source of truth and enables static analysis, code generation, and excellent IDE support.

REST relies on OpenAPI for typing, which is optional. Many REST APIs exist without any formal schema, which means consumers have to read documentation (if it exists) rather than interrogate the API directly.

Versioning

REST APIs are typically versioned in the URL: /v1/users, /v2/users. This is explicit and easy to understand, but it creates parallel API versions that must be maintained simultaneously during deprecation periods.

GraphQL deprecates fields in the schema using the @deprecated directive rather than creating new URL versions. Clients that use deprecated fields continue to work; tooling surfaces the deprecation warning. This is cleaner in theory, though managing a large schema with many deprecated fields has its own complexity.

HTTP Caching

REST caches naturally at the HTTP layer. A GET /api/users/123 response can be cached by CDNs, proxies, and browsers using standard HTTP cache headers. This is one of REST’s most significant operational advantages: you get caching infrastructure for free.

GraphQL uses POST by default (queries include the query string in the body), which is not natively cacheable at the HTTP layer. Persisted queries (where the client sends a query hash rather than the full query text, enabling GET requests for cacheable queries) solve this but require additional implementation effort. Apollo and similar clients support persisted queries, but it is not automatic.

Code Example: The Same Data in REST vs GraphQL

Consider fetching a user with their recent orders. Here is how each approach handles it.

REST: three requests

 1GET /api/v1/users/123
 2Authorization: Bearer <token>
 3
 4Response:
 5{
 6  "id": 123,
 7  "name": "Sarah Clarke",
 8  "email": "[email protected]",
 9  "created_at": "2024-01-15",
10  "role": "customer",
11  "preferences": { ... }
12}
13
14GET /api/v1/users/123/orders?limit=5
15Response: [ { "id": 901, "total": 49.99, "status": "shipped", ... }, ... ]
16
17GET /api/v1/orders/901/items
18Response: [ { "product_id": 42, "name": "Widget Pro", "qty": 2 }, ... ]

GraphQL: one request

 1query UserWithOrders {
 2  user(id: "123") {
 3    name
 4    email
 5    orders(limit: 5) {
 6      id
 7      total
 8      status
 9      items {
10        productId
11        name
12        quantity
13      }
14    }
15  }
16}

The GraphQL version returns exactly the fields specified (no created_at, no preferences, no role) and resolves all three data sources in one HTTP round-trip. For a mobile client on a slow connection, this is a meaningful advantage.

Performance Reality: What the Marketing Leaves Out

GraphQL’s data fetching efficiency is real, but production GraphQL has a well-known performance problem: the N+1 query problem in resolvers.

When a GraphQL resolver fetches a list of users and each user has an orders field, a naive implementation will fire one database query to fetch N users, then N individual queries to fetch each user’s orders. For 100 users that is 101 database queries for what should be 2.

The solution is DataLoader, a batching and caching utility that groups individual lookups into batch queries. DataLoader is not optional for production GraphQL; it is a required implementation step. Every list field in your schema that resolves related data needs DataLoader configured correctly, or your database will suffer under real load.

REST does not have this problem. Each endpoint makes the queries it needs, typically with a single JOIN or a small number of coordinated queries designed by the developer who wrote the endpoint.

The other performance consideration is query complexity. GraphQL allows clients to construct arbitrarily deep queries. A malicious or poorly designed client can send a query that traverses deeply nested relationships and triggers enormous database load. Query depth limiting and query complexity scoring are required security and performance measures for any public-facing GraphQL API.

Security Considerations

REST has a simpler security model. Each endpoint is a discrete surface that can have its own middleware: authentication checks, authorisation rules, rate limiting, input validation. A route-level middleware stack means the security logic for POST /api/orders is self-contained and auditable.

GraphQL’s single endpoint model means all access flows through one point. Authorisation must be implemented at the resolver level, and it is easy to miss. If a user type exposes an adminNotes field and the resolver does not check the caller’s role, that field is accessible to anyone who knows to ask for it. Field-level authorisation libraries (such as graphql-shield) exist, but they require deliberate implementation rather than being the natural structure of the code.

Query abuse is a significant concern for public GraphQL APIs. Without depth limiting and query complexity scoring, a single malicious query can trigger a denial of service. Apollo Server and other runtimes provide these controls, but they must be configured explicitly.

Introspection, which is excellent for developer tooling in development, should be disabled in production for public APIs. It allows anyone to enumerate your entire schema, which is useful intelligence for an attacker mapping your data model.

REST vs GraphQL: Side-by-Side Comparison

CriterionRESTGraphQL
Data fetching controlServer-definedClient-defined
Multiple resourcesMultiple round-tripsSingle request
Over-fetchingCommonEliminated
HTTP cachingNative (GET requests)Requires persisted queries
Type systemOptional (OpenAPI)Mandatory
VersioningURL versioningSchema deprecation
Learning curveLowModerate to high
N+1 problemNot applicableRequires DataLoader
Security modelPer-endpoint middlewareResolver-level authorisation
Tooling maturityVery matureMature
Public API usabilityExcellentGood with documentation

When to Choose REST

REST is the right choice in the following scenarios.

Simple CRUD operations. If your API is mostly create/read/update/delete operations on discrete resources without complex relationships, REST’s resource-oriented model is a natural fit. The overhead of a GraphQL schema is not justified.

Public APIs with external consumers. REST APIs are universally understood. Any developer, in any language, can consume a REST API with a basic HTTP client. GraphQL requires either a GraphQL client library or knowledge of the query syntax. For APIs consumed by third-party developers, REST’s discoverability and simplicity are material advantages.

When HTTP caching is important. If CDN caching, browser caching, or edge caching is part of your performance architecture, REST’s native GET-based caching is a significant advantage over GraphQL’s additional complexity.

Teams without GraphQL experience. GraphQL has a real learning curve: schema design, resolver architecture, DataLoader, query complexity management, persisted queries. If your team does not have experience with it, the productivity cost during adoption is real and should not be dismissed.

Microservices communicating internally. Service-to-service communication within a backend system rarely benefits from GraphQL’s client-defined queries. Each service typically knows exactly what it needs from another service, making REST’s fixed-contract model more appropriate.

When to Choose GraphQL

GraphQL justifies its complexity in specific circumstances.

Complex data graphs with many relationships. Social networks, product catalogues with deep attribute hierarchies, content management systems with complex relationship models: these are the problem spaces GraphQL was designed for. When your data looks like a graph rather than a table, GraphQL’s query model is a natural fit.

Mobile clients where bandwidth matters. Fetching only the fields a screen needs, combining multiple related queries into one request, and avoiding over-fetching are all meaningful on mobile. Facebook built GraphQL specifically because their mobile app was suffering under REST’s data transfer overhead.

Multiple consumers with different data needs. A web app, a mobile app, and a third-party integration may all need different subsets of the same underlying data. With REST you either build multiple endpoints or return a superset and let each client ignore what it does not need. With GraphQL each client requests exactly what it displays.

Rapid frontend iteration. When the frontend team needs to add a field to a screen and the backend team would otherwise need to update a REST endpoint to include it, GraphQL eliminates that coordination cost. The field just needs to exist in the schema (and be resolvable); the frontend team can add it to their query without a backend change.

Internal APIs where you control both ends. The tooling advantages of GraphQL, including code generation, type safety, and schema introspection, deliver the most value when both the client and server are built and maintained by the same team.

The Honest Recommendation for 2026

Most UK software teams building new projects in 2026 will be better served by REST. That is not a dismissal of GraphQL; it is a recognition that GraphQL’s costs are real and its benefits are only compelling in specific circumstances.

GraphQL’s learning curve is steeper than its advocates often acknowledge. Schema design is a skill. DataLoader patterns require understanding. Field-level authorisation requires deliberate architecture. Query complexity management is easy to overlook. None of these are insurmountable, but they add up to a meaningful investment during the initial build, and they must be maintained correctly as the schema grows.

REST’s caching advantages are understated in most comparisons. The ability to put a CDN in front of your API and cache GET responses aggressively is genuinely powerful. Many high-traffic applications serve the majority of their traffic from cache, and REST makes that simple. GraphQL makes it possible with persisted queries, but it requires additional work.

Choose GraphQL when your data is genuinely graph-shaped, when you have multiple clients with divergent data needs, or when your frontend team needs the flexibility to evolve their queries without backend changes. Choose REST when you are building a public API, when your team does not have GraphQL experience, or when HTTP caching is important to your architecture.

The worst outcome is choosing GraphQL because it sounds more modern and then spending the first month of the project building infrastructure that REST would have given you for free.

Key Takeaways

  • REST is the pragmatic default for most projects: lower complexity, native HTTP caching, and universal client compatibility
  • GraphQL’s real advantages are over-fetching elimination, one-request multi-resource queries, and client-defined data shapes; these matter most for mobile clients and complex data models
  • The N+1 problem in GraphQL resolvers is a production reality, not a theoretical concern; DataLoader is required, not optional
  • REST’s per-endpoint security model is simpler to reason about than GraphQL’s resolver-level authorisation; this matters for teams without GraphQL experience
  • GraphQL adds the most value when you control both the client and the server and the data relationships are genuinely complex
  • Pick REST for public APIs, simple CRUD, and caching-heavy architectures; pick GraphQL for complex internal APIs with multiple frontend consumers

Frequently Asked Questions

Is GraphQL replacing REST? No. GraphQL adoption has grown steadily but REST remains dominant for new APIs in 2026. Both technologies are actively developed and both have strong use cases. GraphQL has not made REST obsolete; it has carved out a niche for specific problem types.

Can GraphQL and REST coexist in the same project? Yes, and this is common. A public REST API for third-party consumers alongside an internal GraphQL API for the company’s own frontend products is a sensible architecture. The two approaches serve different needs and can share the same underlying data layer.

Is GraphQL harder to learn than REST? Yes, meaningfully so. REST’s concepts map directly onto HTTP, which most developers already understand. GraphQL requires learning the query language, schema definition language, resolver architecture, DataLoader patterns, and a separate set of security controls. Expect a few weeks for a team to become productive, not a few days.

Does GraphQL have better performance than REST? It depends on the workload. GraphQL can reduce the number of HTTP round-trips, which helps on high-latency connections. But it does not cache as naturally as REST, and a poorly implemented GraphQL server with unoptimised resolvers will perform worse than an equivalent REST API. Performance depends heavily on implementation quality.

What is the N+1 problem in GraphQL? When a GraphQL resolver fetches a list and each item in the list triggers an individual query for related data, you get N+1 database queries instead of the 2 you expected. A list of 100 users where each user resolves their orders individually fires 101 queries. DataLoader solves this by batching individual lookups into a single query per batch.

Which should I use for a new startup API in 2026? REST, unless you have specific reasons not to. Start with REST, document it with OpenAPI, and build on it until you have concrete evidence that GraphQL’s strengths apply to your problem. Migrating from REST to GraphQL is possible; starting with GraphQL and discovering you did not need it means carrying unnecessary complexity throughout the project’s life.