GraphQL is dying and I could not be happier. The hype has faded. The conference talks have dried up. The true believers have gone quiet. Teams are quietly migrating back to REST and pretending they never suggested GraphQL in the first place. The fever has broken.
Good. It was a fucking mistake.
The problem it solved did not exist
GraphQL was supposed to solve over-fetching. Your REST API returns too much data, they said. You are wasting bandwidth, they said. The mobile clients only need three fields but you are sending back fifty.
Here is the thing. You could have just made a new endpoint. You could have added a fields query parameter. You could have done literally anything other than invent an entirely new query language with its own parser, its own type system, its own tooling ecosystem, and its own special class of problems that did not exist before.
But no. Facebook had a problem at Facebook scale with Facebook complexity, and they solved it with a Facebook solution. Then they open sourced it, and the rest of us convinced ourselves we had the same problem. We did not. We had a REST API that worked fine and a vague feeling that we should be doing something more modern.
You traded one problem for twelve
Over-fetching is solved. Congratulations. Now enjoy your new problems.
N+1 queries. Your beautiful nested GraphQL query that fetches users and their posts and their comments and their likes? That is four hundred database queries. You need a DataLoader now. You need to batch things. You need to think about query complexity in ways you never had to think before.
Query complexity attacks. Anyone can send you a query asking for users, with their posts, with their comments, with their replies, with their authors, with their posts, nested seventeen levels deep. Your server falls over. You need depth limiting. You need complexity analysis. You need to defend against your own API.
Caching is a nightmare. REST gives you HTTP caching for free. Every endpoint has a URL. Every URL can be cached. GraphQL? Everything is a POST to the same endpoint with a different body. Good luck caching that. You need a whole new caching strategy. You probably need Apollo. You definitely need a drink.
Versioning does not exist. REST APIs version cleanly. V1, V2, V3. Deprecate the old, maintain the new. GraphQL told you versioning was unnecessary because you can just add fields forever. This works until you need to remove something or change how something works, and then you discover that versioning was actually a feature and not a limitation.
The tooling tax
To use GraphQL properly you need a schema. You need type generation. You need a client library. You need a server library. You probably need code generation to keep your types in sync between client and server. You need Apollo or Relay or URQL or whatever the flavour of the month is.
REST needs fetch. That is it. Fetch. The thing that comes free with every browser and every runtime. You call an endpoint, you get JSON back, you use it. No schema. No codegen. No build step. No thirty megabyte client library.
I have watched teams spend weeks setting up GraphQL tooling. Getting the codegen right. Fixing the types. Debugging why the generated types do not match the actual responses. Upgrading Apollo when a new version breaks everything. This is time that could have been spent building features. Instead it was spent feeding the machine.
And I want to be honest with you: I fell for it too. I advocated for GraphQL. I built GraphQL APIs. I wrote GraphQL clients. I suffered through the complexity attacks and the N+1 queries and the caching nightmares. I drank the Kool-Aid.
Then I woke up. I saw the light. I returned to REST. I have not looked back since.
It made simple things complicated
I want to get a user by ID. In REST, that is GET /users/123. Done. Everyone understands it. The URL is self-documenting. The HTTP verb tells you what is happening. You can test it in a browser.
In GraphQL, that is a query with a variable that you POST to a single endpoint with a JSON body that contains the query string. You need a special client to test it. You need to know the schema to know what fields exist. The URL tells you nothing. The simplicity is gone.
And for what? So you can ask for only the fields you need? You could have done that with a query parameter. So you can nest related resources? You could have done that with includes or embedded resources. Every benefit GraphQL claims to provide has a simpler solution in REST that does not require reinventing how APIs work.
The frontend devs loved it though
I understand the appeal for frontend developers. You define what you want and you get exactly that. No waiting for the backend team to add a new endpoint. No negotiating over response shapes. The schema is the contract and you can explore it in a playground.
But this came at a cost. Someone still has to implement the resolvers. Someone still has to optimise the queries. Someone still has to deal with the complexity on the backend. That someone is usually a different team, and they are usually drowning.
GraphQL did not eliminate coordination between frontend and backend. It just shifted the burden. Frontend got a nicer DX. Backend got a query language to parse, a type system to maintain, and a whole new category of performance problems to solve. The work did not disappear. It moved.
Facebook does not even use it properly
Here is the best part. Facebook, the company that created GraphQL, does not use it the way everyone else uses it. They do not expose a public GraphQL API. They do not let clients write arbitrary queries. They use persisted queries where the allowed queries are defined ahead of time and the client just sends an ID.
This sidesteps most of the problems. No complexity attacks because the queries are fixed. No N+1 surprises because the queries are optimised. No caching nightmares because the queries are known.
But this is not how GraphQL was sold. It was sold as dynamic, flexible, client-driven queries. The thing that made it appealing is the thing that makes it dangerous. Facebook kept the good parts and threw away the footguns. Everyone else adopted the footguns and wondered why they kept shooting themselves.
REST won and that is fine
REST is boring. REST is old. REST does not have a cool playground or a type system or a query language. REST is just URLs and HTTP verbs and JSON responses. It has been around for decades and it will be around for decades more.
And it works. It scales. It caches. It versions. It requires no special tooling. Junior developers understand it immediately. Senior developers do not have to debug query complexity issues at 2am. The simplicity is a feature, not a limitation.
The industry is coming back to REST because REST was never the problem. Over-fetching was not the crisis GraphQL made it out to be. Under-fetching was solvable with better endpoint design. The problems were smaller than the solution.
GraphQL will stick around in niches where it makes sense. Internal APIs with many frontend consumers. Complex data requirements that genuinely benefit from flexible queries. But as a default choice for new projects? That era is over. The hype cycle has completed. We are in the disillusionment trough and there is no plateau of productivity coming.
REST won. Long live REST. And good fucking riddance to the GraphQL era.