Why not use GraphQL?
Join our Open Source Community!
Join our Discord community to explore the exciting world of Backend for Frontend (BFF) architecture! We're a group of web development enthusiasts who are passionate about creating scalable and efficient APIs that power modern web and mobile apps.
I think GraphQL will change the world. There will be a future where you can query any system in the world using GraphQL. I'm building this future. So why would I argue against using GraphQL?
My personal pet peeve is when the community keeps advertising benefits of GraphQL that are very generic and really have nothing to do with GraphQL. If we want to drive adoption, we should be honest and take off the rose-tinted glasses. This post is a response to "Why use GraphQL" by Kyle Schrade (https://www.apollographql.com/blog/why-use-graphql/). It’s not meant to be direct criticism. The article is just an excellent base to work with as it represents opinions I keep hearing a lot in the community. If you read the whole article, it’ll take some time, you’ll fully understand why I think Kyle’s article should be named “Why use Apollo”.
If you haven't read Kyles's article already, I think it makes most sense if you read it first: https://www.apollographql.com/blog/why-use-graphql/
Downside of REST
The author states that REST APIs come with a set of downsides and how GraphQL solves all of them: Over-fetching Multiple requests for multiple resources Waterfall network requests on nested data Each client need to know the location of each service
The first three issues could be solved by writing another REST API as a facade for a specific user interface. Take Next.JS as an example. Next lets you define APIs with a very lightweight syntax. Instead of making multiple requests from the client, you can wrap those calls into an API and make them server-side. Over and underfetching can be solved with this approach too, as you can manipulate the data before sending it back to the client. The pattern described is named "backend for frontend" (BFF). It's not limited to full stack frameworks like Next.JS. You can build a BFF for your mobile apps as well.
With the BFF pattern, the client itself doesn't have to know the location of each service. However, the developer who implements the BFF needs to understand the service landscape. Hopefully you have Open API Specifications for all your services, nicely presented in a developer portal. If that's the case, it should be easy to write a BFF.
With GraphQL, there still needs to be a developer who implements the resolvers. Implementing the resolvers is more or less the same task as building a BFF, the logic is very similar. So, what's the real difference?
The BFF is easier to implement as there's a lot more tooling available. E.g. if you use a framework like Next.JS in combination with swr hooks (stale while revalidate) you get automatic caching with Etags and cache invalidation out of the box. This reduces the amount of data sent between server and client. It's even less data than GraphQL, because you're not sending query payloads and the server responds with 304 (Not Modified) if the response is still valid. Additionally, you don't have to use a heavyweight client like Apollo. The library swr by Vercel is small and very easy to use. It comes with support for pagination, hooks, and helps to navigate back and forth very efficiently.
GraphQL has persisted queries but it comes with additional overhead to implement this. If you don't use a client like Relay, which persists Queries by default, you have to do it on your own or use some third party library to implement it. Compared to the BFF approach using e.g. Next.JS there's a lot more complexity involved in getting to the same results on the frontend. How would you implement Etags with GraphQL? How do you make your GraphQL server return 304 status codes if nothing changed? Don't you first have to turn all Queries into GET requests? If so, does your GraphQL client and server easily support this?
When it comes to user experience and ease of development, the BFF is the clear winner. Less data transfer between client and server. Easier to implement. Smaller client, less moving parts.
But there's a catch. You have to build a BFF for each individual frontend. If you have many of them this can be a lot of work. You have to maintain all the BFFs. You have to operate them. You have to secure them.
Wouldn't it be nice if you could have the benefits of both without making tradeoffs? This is exactly what WunderGraph is. A framework to build BFFs using GraphQL.
No more versioned APIs
In the next paragraph, Kyle goes on with the problems involved with versioned APIs. He's absolutely right that having too many versions of an API makes it very hard to keep track of. He then concludes that in GraphQL, there's only one version of the graph and changes can be tracked in a schema registry, a paid feature of Apollo. For that reason you won’t have any problems with versioning, he says.
I have problems coming to the same conclusion. Just because GraphQL schemas don’t support versioning natively doesn’t mean the problem goes away. You get the same effect if you just don’t version your REST APIs. In fact, many experts say that you should always try to not introduce versions of an API if you don’t have to. That being said, what holds you off running two versions of your GraphQL schema? Not that I think this is a good idea but it's technically possible.
If having too many versions of your REST APIs is a problem in your organization, before throwing a new tool like GraphQL at the problem, maybe you should have a look at the organization first. What are the reasons for having so many versions? Maybe the change of a process or new team structures can help? GraphQL does absolutely nothing to solve your versioning problems. Instead I think it actually makes the situation worse.
Do you have to support mobile applications? You should be aware that shipping native apps takes time. You have to wait for app store approval and you can expect many of your users to never (or slowly) install the new version. What if you want to introduce a breaking change in this scenario without breaking a client? It's impossible. You have to introduce this change in a non-breaking way. It would be interesting to hear from Facebook how they avoided breaking clients.
Evolving your schema in the case of GraphQL would mean, you deprecate the old field and add a new one. New clients use the new field while you hope that the number of clients using the old field will get less and less. Hopefully, you have a system in place that forces your users to download a new version at some point in time. Otherwise, you might be forced to support the deprecated field indefinitely. If that's the case, the deprecation model of GraphQL doesn't help you at all.
With REST you could create a new endpoint or another version of an existing one. The problem is the same, the solution just looks a bit different.
To make it clear, if you cannot control your clients you really want some kind of versioning. If all you have is a single web application you won’t need this feature. But then again GraphQL might be overkill as well.
In this paragraph, the author states that RESTful APIs don't allow partial responses.
This is just wrong. Here's an example:
What does the author actually mean? I'm pretty sure he's aware of partial responses. I guess what he's trying to say is that someone needs to implement partial responses. Actually, it looks very familiar to GraphQL as you're selecting subfields from a resource. With GraphQL we have this feature out of the box.
On the other hand, with the BFF approach, you don't need this. Just return exactly the data you need. Again, a full-stack framework like Next.JS makes it simpler to implement this, makes caching easier and gives you Etag based cache invalidation for free.
To sum this section up, GraphQL gives you exactly the data you need. Partial responses can achieve the same result. BFFs come with the additional cost of implementation and maintenance but have a better UX & DX.
In this paragraph, Kyle addresses the issues of REST APIs not being strictly typed. He talks about the problems with APIs where it's not clear if you get an array of posts or something different and how query parameters complicate the situation. He also states that GraphQL, because of its strict type system, doesn't have this problem.
I think what Kyle is talking about is an organizational problem for which you need an organizational solution.
You have the kind of problems he describes, when you allow developers to deploy REST APIs without publishing Open API Specifications (OAS) or similar. With OAS all resources can be described very easily. OAS also allows you to describe OAuth2 flows and required scopes per endpoint. Additionally, you can describe the exact types and validation rules for query parameters, a feature that GraphQL is lacking.
Looking at GraphQL, there's no way to describe Authentication, Authorization and input validation. GraphQL is lacking these features because the inventors at Facebook solved this problem at a different layer. There was no need for them to add these features to GraphQL. You can add custom directives to your schema to achieve similar results like OAS but this would be a custom implementation which you have to maintain yourself.
You might be thinking that OAS doesn't guarantee the response of an API to be compliant with the specification. You would be right. But how does a GraphQL schema guarantee anything?
GraphQL introspection is the act of sending a specific GraphQL query to the server to get information about the GraphQL schema. The GraphQL server is free to answer with whatever types it wants to. If you send a Query, the server can answer with a response that doesn't adhere to the GraphQL schema from the introspection response. Take Apollo Federation as an example. You upload your schema into a schema registry and then, by error, deploy the wrong version of your GraphQL server. If you change the type of a field, the client might be confused.
When we talk about type safety in GraphQL, what we actually mean is that we trust in a GraphQL server to behave exactly as advertised by the introspection Query response. Why can't we trust an Open API Specification in the same way? I think we can. If we don't, we have a people problem, not a technical one.
Better client performance
The next paragraph, a short one, is about how GraphQL improves client performance and reduces network round trips.
I think I've explained more than enough how much more powerful a BFF is and how much you gain from the ‘stale while revalidate pattern’ compared to a heavyweight GraphQL client.
That said, GraphQL does indeed reduce the number of requests and reduces the overall data transfer. However, you should always consider the cost of adding a GraphQL client to your frontend.
Less time spent documenting and navigating APIs
The next section is about how tools like OAS are being used for RESTful API development and the challenges of maintaining multiple OAS in microservice environments. Kyle compares a single GraphQL schema with Swagger files spread out across multiple git repositories.
I think it's clear that navigating a single GraphQL schema is a lot simpler than looking at multiple OAS files, sitting in git repositories. However, to be fair, we have to compare apples to apples. If you want to make your developers productive, you wouldn't stick OAS files into a git repository and call it a day. You would run a developer portal where you can search for APIs and navigate between them.
OAS relies on JSON-Schema which comes with an amazing feature: You can reference object types from another document. You can divide your OAS into multiple files which reference each other if required. There's also tooling to combine multiple OAS files into a single OAS document. You could then use this document and feed it into a developer portal which lets you explore all APIs as a whole. Keep in mind that there's the additional cost of setting up all this. You need to run a dev portal or buy one. You have to describe all your APIs, which, at least at the beginning, can be a burden.
One thing to add, there are a lot of frameworks that let you describe a schema in your favourite programming language, e.g. through defining Objects or Classes. You'll then get an auto-generated Open API specification served at a well-known endpoint.
Let's compare that to GraphQL. There are basically two approaches, SDL first vs. code first. Whatever way you go, you end up with a GraphQL schema which describes all your types as well as fields and allows you to comment on them.
So, what's the difference then? OAS comes with more overhead to set things up. On the other hand, OAS has built-in support documenting example use cases, authentication & authorization as well as input validation rules.
Keep in mind that GraphiQL itself has no concept of multiple GraphQL schemas. If you have multiple GraphQL (micro-)services you have to run or buy a dedicated component, e.g. a schema registry which is similar to a developer portal for REST APIs.
One thing I wanted to dedicate an extra paragraph is API use cases. Just because you have an OAS or a GraphQL schema doesn't mean your API is well documented. What can an API user do with the API? How can they use it? What are good use cases? What are the bad ones? Where to ask for help? How do I authenticate a user? Do I need an API key? Documenting your API in a way that helps API consumers use it is much more work than adding descriptions to types and fields. OAS allows you to add example payloads and describe them. GraphQL is lacking this feature. Look at Stripe for a positive example of this. They went way beyond what a Swagger or GraphQL Playground could ever achieve.
If, on the other hand, you look at the public GraphQL API of GitHub you will find not a single example Query. Let's say you want to get information about a repository and all its issues. You'd have to open GraphiQL and start searching. However, the search functionality in GraphiQL doesn't really help you much. Someone needs to sit down and write example Queries and use cases on how to use the API. Otherwise, it's really hard to get started.
So, while the community keeps saying "GraphQL is self-documenting" this feature alone doesn't make a useful API. OAS gives you a tool to add use cases but you still have to write them. It takes effort to make an API useful to others, no matter what tools or languages you chose.
Legacy app support
In this paragraph, the author says that keeping around old versions of REST APIs for mobile apps is a pain. He concludes that because we're only using a single GraphQL server, we don't have this problem.
I'm sorry, but again, I get to a completely different conclusion. If you set the rule to disallow versioning, you can add new endpoints or swap the implementation of existing ones. There is no difference between GraphQL and REST in this case. Supporting legacy apps is a challenge with both REST and GraphQL APIs. You need to find a way to not break the contract between client and server. It doesn't matter if your server exposes REST or GraphQL, the problem is the same.
Better error handling
On error handling, the author describes a scenario where a client would have to make 3 subsequent REST API calls compared to a single GraphQL query that would respond with partial data.
With GraphQL, the logic of resolving partial data sits in the server. The client needs to have additional logic to react to a partial response appropriately.
With REST the logic of fetching partial data could sit in the client or in a BFF. Either way, the logic is more or less the same as with GraphQL, it just sits somewhere else. Obviously, the REST API use case also needs logic in the client to handle a partial response. This logic will be almost identical to the one in the GraphQL use case.
Nothing holds you off from returning specific information in a REST response on why something failed. OAS allows union types so you're free to give rich information to the client about a partial response. This is similar to the concept of response unions described by Sascha Solomon (https://sachee.medium.com/200-ok-error-handling-in-graphql-7ec869aec9bc).
Does GraphQL really have better error handling? I think both OAS and GraphQL give you good tooling to handle errors in a very user-friendly way. It's up to the developer to make good use of these tools. There's no free lunch.
Kyle concludes the whole article by saying that GraphQL is the future of APIs because it's superior in terms of performance, payload size, developer time and built-in documentation.
I agree that GraphQL is the future of APIs, but for different reasons.
Better performance and smaller payload size are no unique features to GraphQL. You can achieve better results with other tools or have to extend GraphQL, e.g. using Relay to get persisted Queries. To get real benefits from GraphQL documentation you definitely have to do more than just adding descriptions to your schema.
Once the dust is settled and the hype is gone, we have to look at the facts. We should not try and convince the world that GraphQL is something which it isn't. There are a lot of advantages of using GraphQL but depending on your use case you might not really benefit from them. Instead of advertising GraphQL as the holy grail, we should give a more nuanced response.
The best way to tackle this is to look at the problem first and then make a distinctive comparison of possible tools to solve a problem. If your organization fails at implementing REST APIs, how does GraphQL solve this problem? Maybe something within your organization has to change? If, on the other hand, it's not an organizational problem, and you're absolutely sure REST is no good alternative for your use case, I bet you will love the developer experience of GraphQL.
My version of "Why GraphQL"
GraphQL in itself is barely useful. It’s the tools that make GraphQL so powerful. It’s the community, it’s us! It’s companies like Apollo, Hasura, The Guild, FaunaDB, Dgraph, GraphCMS and I hope WunderGraph too, who make GraphQL so powerful. It’s GraphiQL, various GraphQL clients, Schema Stitching, Federation. This whole ecosystem of tools is the reason why GraphQL is the next big thing.
More tools and services will strengthen the ecosystem. A stronger ecosystem will lead to more adoption which again will lure in more companies to add services and tools to the GraphQL ecosystem, a very positive loop.
GraphQL in that regard is very similar to Kubernetes. Docker, the container runtime, wasn’t enough. Wrapping complex Syscalls into a simple to use API was the enabler but in order to create a rich ecosystem a scheduler was needed that was expressive enough and allowed very easy extensibility.
The language open-sourced by Facebook is not what made it successful. It’s tools like Relay that did. Unfortunately, many of the tools used internally never made it to the public. The community had to catch up with this which I think we’ve got pretty far already.
My personal Conclusion
When Kyle asks “Why GraphQL” I think what he actually means is “Why Apollo”. And the answer is simple. No one cared to build a rich ecosystem around REST APIs. Try to generate a React.JS client from an Open API Specification. The experience sucks, compared to what GraphQL clients give you. No one figured out a good business model to solve the problem. Enter GraphQL and you get a massive amount of tools to abstract away problems you don’t want to deal with.
REST APIs will become less relevant for the described use cases not because GraphQL is superior. It’s the tools and ecosystem that will make GraphQL continue to gain market share. The pace at which the GraphQL ecosystem is expanding is massive compared to REST.
Do you think GraphQL would have become what it is without all the tooling by Apollo? What about the conferences? GraphQL Summit? Hasura online conferences? GraphQL in Space by Dgraph? What about the massive open source contributions by The Guild? GraphQL Galaxy?
I hope this gave you a more nuanced view of why you should use GraphQL. A less hype-loaded view which should prepare you well to convince your manager.