GraphQL Is a Trap?
Here&#x27;s a list of every advantage I&#x27;ve heard about GraphQL. All of these advantages were used as evidence to push a transition internally at my organization. Many regret it at this point, not to spoil the conclusion, but:<p>(1) Its schema driven. Schemas for your APIs are very good.<p>Sure are. And this argument is always listed with no hint of irony or even recognition that there are dozens of schema-driven API systems out there. GraphQL is one. GRPC, protobuf, JSON-RPC, OpenAPI, I mean the list goes on.<p>And so, realistically, where you hear this is from people who worked in an organization who wasn&#x27;t codifying their APIs with a schema, and now they want one. GraphQL has schemas, so its an advantage of GraphQL; but this advantage needs to be rephrased to really mean: schemas are good, and there are many ways we can get a schema.<p>(2) (Extension of the last) We can use the schemas to auto-gen client libraries and server adapters; look at this cool graphql-codegen tool.<p>Same response. I can&#x27;t even stress how many times I&#x27;ve had this conversation. Its like we&#x27;re relearning the same thing every three years as people enter and leave the industry, without any recognition of existing, far more mature, stable, and excellent tooling.<p>(3) There&#x27;s so much query boilerplate around every REST API we have. Pagination, filtering, selection, querying, reduction, analytics, tokenization; GraphQL solves that for us.<p>It doesn&#x27;t. It solves Field Selection. That&#x27;s it. Your new GraphQL API will have 90% of all the same boilerplate.<p>(4) It makes frontend life easier, because we don&#x27;t have to rely on the backend to add new fields or relationships.<p>Yes and No. Our experience has been this: data is more-immediately available to frontend teams, yes. If they need a relationship between Users and Comments, that probably already exists, and they can get it in one call; its not a bad setup from that perspective.<p>But really drive into the advantage here: its not that the relationship exists in graphql and wouldn&#x27;t in rest. Of course it would exist. Its that it exists <i>in one call</i>. That&#x27;s literally the only advantage graphql gives; not that it exposes more data, but that it grants the API the ability to construct dynamic views on the data.<p>So, drive further. Views. Why would we use a view in, say, Postgres? Usually performance. The issue arises, and the reason why this is only a partially strong argument for GraphQL, in that these dynamic views GraphQL enables frontend to generate are non-optimized, the majority of the time. After all, its combinatorial; the API team can&#x27;t predict every way data will be accessed.<p>Well, the funny thing is; they can&#x27;t predict it, but they can still optimize these views. By being a dependency of frontend team work. Congratulations; we&#x27;re back to square one; and this is LITERALLY how I&#x27;ve seen 80% of frontend projects play out on GQL. Either some data wasn&#x27;t on the graph and we need it; or a mutation wasn&#x27;t there which is necessary; or the data is there, and it worked locally, but in production for customer X its too slow because performance, so we need backend optimizations.<p>(5) (Extension) These dynamic views save on internet round-trips.<p>This is a complex one. If you want the spoilers: distributed systems are insanely complex, and I&#x27;m hesitant to say graphql is &quot;better&quot;; its just different, maybe better in some situations, maybe worse in others.<p>First: there are non-zero advantages to having the front-end make two+ API calls (especially if they can be done in parallel, which isn&#x27;t always possible I fully recognize). The main one is horizontal scaling; the difference between a GraphQL request 3 layers deep, and 3 API calls, is that those 3 API calls can be handled by 3 different service replicas.<p>We have, on the API team, on too many occasions to count, done an audit of the GraphQL requests hitting our servers, and found absolute monstrosities which destroy P95 response times. We may, then, implement some kind of limiter; you can only go X layers deep, or select Y number of fields, or whatever. These are, to my eyes, weird! Its like saying &quot;yea GraphQL is open ended, request whatever you want, combine two calls into one, uh oh you requested too much, you must have missed that sidenote in our documentation, now you have to make two serial calls, and we&#x27;re back to REST&quot;. You can blame this on a poorly designed API; but that&#x27;s kind of my point; GraphQL encourages poorly designed APIs.<p>To be clear, so does REST. Again, my point: GraphQL isn&#x27;t better.<p>I say &quot;too many occasions to count&quot; because determining what a &quot;good&quot; GraphQL request &quot;limiter&quot; is, is an intractable problem. Should we allow 3 layers deep? 4? 20 fields per object? Doesn&#x27;t it differ depending on the object; some are more expensive than others? How do we encode that &quot;expensiveness&quot;? There are tools which help with this; some of these ask you to assign a &quot;expensiveness&quot; score to every query, mutation, object, etc, and then it allows each incoming query an aggregate &quot;maximum expensiveness&quot; before rejecting the request. Literally. Try communicating that to your users! How?!<p>Moreover, if it wasn&#x27;t already complicated; its so, so difficult to know where the limiter should be placed, for a given API, before the API is already in use and &quot;broken&quot; by someone. Think about that for a second; we have to circle back and say &quot;yeah, we know that request was ok, if not slow, yesterday; but today we have to break our API for you.&quot; This is GraphQL. Its a constant battle due to the combinatorial expansion of ways users can access your API.<p>The second point I want to drive into here is: caching. Its very clear that one internet roundtrip is better than two, all else being equal. But GraphQL doesn&#x27;t make all else equal. One of the fantastic things about more standard HTTP APIs is their high-cacheability. GraphQL has a strong client caching story (though, every six months our frontend teams go heads-down on upgrading to the latest major version of whatever caching library we use; its a nightmare for them). But what about edge caching?<p>GraphQL is DESIGNED to not be cacheable. Edge caches can&#x27;t parse the internal structure of the request bodies (and even if they could, its unclear if they&#x27;d want to; its such a complex language). Identical requests can be structured entirely differently; did they order fields differently, did they name the operation differently, parameters in different places, etc. HTTP APIs can experience some of this (example: query parameter positioning), but most edge caches can handle that; edge caches can&#x27;t handle GraphQL.<p>So the question really isn&#x27;t: is one network hop better than two? Its: is two network hops, to an edge cache ten miles away with an 80% hit rate, better than one to my data center in Ashburn Virginia? NOW its less clear that one is better than the other. There&#x27;s too many variables to say for sure.<p>Here&#x27;s some other random negatives of GraphQL I&#x27;ll throw on:<p>* N+1 queries by default. Someone will link some other library you have to add on which &quot;fixes&quot; this (where, fixing it really means, just adding more work for the backend teams on every new API, every library update, every library CVE, at some point we&#x27;ll figure out that more code rarely solves problems on any time horizon except &quot;right now&quot;).<p>* There are practically speaking no mature GraphQL servers outside of javascript. I legitimately don&#x27;t know any major organizations who write the majority of their backend code in javascript. I know tons of minor organizations who do, because their hiring problems outweigh their technical ones. The major orgs I know who deploy GraphQL APIs do a gateway pattern; a thin shim of JS in front of their other networked traditional APIs. Ok, fine; not exactly transformative. Schema stitching is a joke; a solution in search of a problem extremely few have.<p>* That being the case: in Apollo Server + TypeScript; resolvers are extremely difficult to get strong typing on, and make it extraordinarily difficult to get contextual information about other layers of the request within a resolver. &quot;What object did my parent&#x27;s parent&#x27;s resolver resolve to?&quot; sometimes that needs to be answered. In REST-land; it would just be two-or-three requests; the answer is in the grandchild request&#x27;s parameters, and orchestrated by the frontend. This is <i>easy</i>. But the frontend wants to turn an easy problem into a problem that requires no thought, and in doing so forces the backend to take an easy problem to a difficult problem.<p>Look; this a pattern you see all over GraphQL once you start using it; its invented by frontend people to make their lives easier, at any expense, including raising the net complexity of the overall system. Its a layer on top of HTTP. So, to convince me to adopt it over standard HTTP, it needs to simply be better. And I&#x27;m not convinced of that. I was when we adopted it. But we&#x27;ve learned better since then.