GraphQL launched at Facebook in 2015 and the engineering community spent the next five years either convinced it would replace REST or convinced it was overengineered nonsense. Both camps were wrong in interesting ways.
The hype peaked around 2019-2020 when every startup seemed to be building a GraphQL API regardless of whether it fit the problem. Then the backlash: blog posts about N+1 queries, complex authorization logic, caching difficulties, and tooling that required significant investment to make production-ready.
In 2026, the teams using GraphQL are using it because they have the specific problem it was designed to solve. The teams using REST are using it for the same reason. The wars are over.
What GraphQL Actually Solves
GraphQL was invented at Facebook to solve a specific problem: multiple clients (iOS app, Android app, web app) needed different shapes of data from the same API. Each client was making multiple round trips and getting too much data because the REST endpoints were designed for the most demanding client.
The symptoms of this problem:
- Over-fetching: clients receive fields they don’t use
- Under-fetching: clients need to make multiple requests to assemble a view
- Endpoint proliferation: adding
?fields=id,name,emailquery params to REST to let clients specify what they need - API versioning that doesn’t actually version
GraphQL addresses all of these by letting clients specify exactly the shape of data they need in a single request. The server returns exactly that shape, nothing more.
When GraphQL Wins
Multiple clients with divergent data needs: If you have a mobile app that needs a lean response and a web app that needs richer data for the same conceptual resource, GraphQL is correct. The alternative is maintaining two REST endpoints or over-fetching on mobile.
Aggregation across multiple services: GraphQL Federation (from Apollo) lets you define a single schema that pulls data from multiple microservices. The client makes one query, the federation layer fans it out, and the client gets a unified response. This is genuinely useful.
Developer experience for internal APIs: Teams using GraphQL for internal service-to-service APIs report faster front-end development - engineers can query exactly what they need without coordinating with the backend team. The self-documenting schema and GraphQL Playground are legitimate advantages.
Content-heavy applications: Contentful, Sanity, and other headless CMS platforms expose GraphQL because content editors create arbitrary content structures that clients need in different shapes.
When REST Wins
Simple CRUD APIs: If your API is create, read, update, delete on a few resources with straightforward relationships, REST is simpler to implement, simpler to debug, and simpler to cache. GraphQL’s flexibility has a cost.
Public APIs: REST APIs are easier to cache at the network level (CDNs, browser caches) because the URL encodes the resource. A GraphQL POST /graphql query can’t be cached by a CDN without custom configuration. If you’re building a public API that needs to handle high traffic cheaply, REST + aggressive caching is better.
Teams without dedicated API expertise: GraphQL’s security model is non-trivial. Depth limiting, complexity analysis, query allowlisting, rate limiting, and authorization at the field level all require explicit implementation. REST makes some of these easier by default.
File uploads and streaming: REST handles file uploads cleanly with multipart forms. GraphQL’s file upload specification is a workaround that most implementations handle inconsistently.
The N+1 Problem and How to Solve It
The most common GraphQL production issue is the N+1 query. Classic example: fetch a list of 100 posts, each with their author. Without batching:
- 1 query for posts
- 100 queries for authors (one per post)
This is a 100x overhead compared to a join. The solution is DataLoader - a batching and caching library:
const authorLoader = new DataLoader(async (ids) => {
const authors = await db.users.findByIds(ids);
return ids.map(id => authors.find(a => a.id === id));
});
// In resolver:
author: (post) => authorLoader.load(post.authorId)
DataLoader batches all authorId loads that happen in the same tick into a single query. The 100 queries become 2 (posts + batch of authors). This is the correct implementation and should be standard in any production GraphQL setup.
GraphQL Federation in Practice
Apollo Federation and GraphQL Mesh are where GraphQL’s power shows at scale. A company might have:
- User service (owns User type)
- Order service (owns Order type, references User)
- Inventory service (owns Product type, referenced by Order)
With federation, each service owns its part of the schema. The gateway stitches them together. A client query like:
query {
user(id: "123") {
name
orders {
id
items {
product { name, price }
}
}
}
}
Executes as coordinated calls to three services, assembled by the gateway. The client sees one schema, one endpoint, one response.
The operational complexity is real - you’re now operating a gateway and N service schemas. But for a platform with multiple teams owning their service contracts, the alternative (coordinating N REST API versions across teams) is often worse.
The State of Tooling
The GraphQL tooling story improved significantly:
- Pothos (formerly GiraphQL) makes TypeScript-first schema building clean
- Prisma + Pothos is a solid full-stack TypeScript combination
- GraphQL Yoga is a modern server that replaced Apollo Server for many teams
- Stepzen and Grafbase offer hosted GraphQL on top of existing databases
The original criticism that GraphQL required a large tooling investment has softened. You can build a production-ready GraphQL API in a day with modern tools.
Bottom Line
GraphQL is not REST with extra steps - it’s a solution to a specific problem: clients with divergent data needs querying shared data. If you have that problem, GraphQL is genuinely better than the REST alternatives. If you don’t have that problem, GraphQL’s complexity (N+1 risks, caching difficulties, authorization surface) isn’t worth it. The 2026 engineers using GraphQL successfully are the ones who chose it for the right reasons, not the ones who chose it because it was new.
Comments