Two years ago I was the GraphQL evangelist on my team. Every new project, I'd push for GraphQL. The flexibility! The type safety! No more over-fetching! I had the talking points memorized.
I've changed my mind. Not completely, GraphQL still has its place. But I now think REST is the better default choice for the vast majority of projects. Here's what shifted my thinking.
The Promise vs. The Reality
GraphQL's pitch is compelling. Clients ask for exactly the data they need. No more, no less. One endpoint instead of dozens. Strongly typed schema that serves as documentation. Self-introspecting API that tools can explore.
In practice, I found the implementation cost much higher than expected. Setting up a GraphQL server requires resolvers, schema definitions, and a query execution engine. You need to handle batching to avoid the N+1 problem (DataLoader exists for this, but it's another thing to set up and maintain). Authorization logic that was straightforward in REST endpoints becomes complex when the client can query any combination of fields.
For a REST API, I can go from zero to a working endpoint in minutes. For GraphQL, there's significant boilerplate before I can serve my first query. On a team with tight deadlines, that setup cost matters.
Caching Is Harder
This is the thing nobody mentions in the "why you should use GraphQL" blog posts. HTTP caching is built into REST. You set cache headers, and everything from CDNs to browser caches to reverse proxies knows how to cache your responses. GET /api/users/123 can be cached trivially by URL.
GraphQL uses POST requests for everything, so standard HTTP caching doesn't work. You need application-level caching (like Apollo Client's normalized cache) or move to persisted queries with GET requests. Both add complexity. For read-heavy applications where caching is critical, this is a meaningful drawback.
I worked on a project where we had to bolt on an entire caching layer that we wouldn't have needed with REST. The time we "saved" by not creating multiple endpoints was eaten up by caching infrastructure.
The Over-Fetching Problem Is Overstated
The main argument for GraphQL is that REST over-fetches data. Your user endpoint returns 20 fields but you only need 3. Fair point. But in practice, I've found a few things that make this less of an issue than it sounds.
First, you can add query parameters to REST endpoints. GET /api/users/123?fields=name,email,avatar solves the over-fetching problem for the 10% of cases where it actually matters.
Second, most of the time, the "extra" data in a REST response is negligible. An extra 500 bytes of JSON is not the bottleneck in your application. The database query and network latency dwarf the payload size difference. I've seen teams optimize their API payloads to save a few kilobytes while their frontend bundles are 2MB of JavaScript.
Third, separate REST endpoints for different views is actually fine. /api/users/123/summary and /api/users/123/full is straightforward, easy to cache, and easy to understand. It's not as elegant as GraphQL's approach, but it works and it's simple.
Where GraphQL Actually Makes Sense
I'm not saying GraphQL is bad. There are cases where it's clearly the right choice.
Multiple client types with different data needs. If you have a web app, a mobile app, and a TV app all consuming the same API, and each needs different slices of data, GraphQL's flexibility is genuinely valuable. This is Facebook's original use case, and it makes perfect sense there.
Complex, deeply nested data. If your data model has lots of relationships and clients frequently need to traverse them, GraphQL handles this more elegantly than REST. Fetching a user, their posts, the comments on each post, and the author of each comment in a single query is cleaner in GraphQL.
Public APIs with diverse consumers. If you're building an API for third-party developers and you can't predict how they'll use your data, GraphQL gives them the flexibility to get what they need without you having to create endpoints for every possible use case.
My New Default
For most projects I work on, REST is the better choice. It's simpler to implement, easier to cache, easier to debug (just curl the URL), and the ecosystem is more mature. Most HTTP tools, monitoring systems, and API gateways are built around REST patterns.
I reach for GraphQL when the data model is complex, when there are multiple client types, or when a public API needs maximum flexibility. But those cases are maybe 10-15% of the projects I work on. For everything else, a well-designed REST API with clear resource naming gets the job done with less complexity.
The best technology is not always the most sophisticated one. It's the one that solves your specific problem with the least friction. For most APIs, that's still REST.