Initial Impressions of GraphQL … from a REST guy’s perspective
As an engineer, I’ve said the following sentence more times than I care to remember:
This feels incredibly broken. I can’t believe no one has come up with a better solution.
I distinctly remember being extremely frustrated while working on my first REST API, because of a few recurring reasons:
1. Making a new request to get each resource individually.
GET /authors
GET /author/:authorId/books
GET /author/:authorId/book/:bookId/comments
Sounds familiar? That’s coz most REST services use this approach. It works … but it’s incredibly painful for a frontend engineer, and in most cases, will end up in really convoluted code.
2. Formatting the data to be in a specific format.
So we have the above data. Now what? What do we do with 3 separate payloads? How do we render the following pseudo-code?
for each author:
render (author name)
for each book of author:
render (book title)
render (comments)
Unfortunately, since the data is not formatted in the above format, there is no straightforward way to render it. The 2 options are as below:
- Manually massage the data, so that the 3 resources requested belong to a nested object.
- Or, have 1 global JSON model that has all of this information. (Horrible idea, please don’t do this). This leads to the REST over-fetching problem.
Then came GraphQL
With a simplistic, declarative, syntax like this:
getAuthors {
name
books {
name
title
comments {
id
response
}
}
}
… and that’s it! I have 3 separate resources (or rather, 3 separate models in my ORM, and 3 separate tables/collections in my database). We define the relationships, and GraphQL resolves each field. Only fetch the required associations, and avoid multiple round trips to the backend!
My first impression as a naive developer was “That’s it! This is the end of REST.! No one is going to use this legacy technology anymore.”
But, over the last few months, having learned quite a few things (and nuances) about GraphQL, I’ve been forced to revisit my initial impressions.
In my mind, even though GraphQL is absolutely awesome as a client-side query language, and solves a bunch of problems (over/under fetching, pre-formatted data, a single endpoint to get everything, developer simplicity etc.), my experience wasn’t as positive when it came to the backend. Here are a few reasons why I think someone should not consider GraphQL ….. just yet :)
Problems with GraphQL
1. No idiomatic way of authorizing resources.
In a traditional REST API, one of the first things your handler/controller does is authorize whether the current context/user, has access to take action x on the resource y, with id z (in ABAC). The pseudo-code for this usually looks like
authorize(context, READ , models.Author, 1);
The above example is checking whether the request allows the context (or the logged in user), to READ, from table Author, where id=1. If not, the request usually will end up in a 403. This gives the API extremely granular access control capabilities.
With GraphQL, there is no idiomatic way of doing this. There is only 1 API request, which might request access to multiple different resources (as we see above), which makes it extremely difficult for your policy enforcement point to determine whether to allow the request or not. Not to say that this isn’t possible with GraphQL, but it hasn’t been standardized yet.
2. No (or near impossible) server-side caching.
With a REST API, since you know EXACTLY which resource (and filtered by what criteria) is being accessed, it’s relatively simple to add a caching layer to your data fetching layer.
With GraphQL, without writing a query parser, it’s difficult to know which resource & fields (and which associations) are being requested. Which is what has led to client-side caching. Why client-side caching is helpful, but not recommended, I’d love to cover in a separate post.
3. Scaling, and speed.
Last, and probably my biggest beef with GraphQL so far.
With REST, you can rate limit separate endpoints, use JOINS to create complex nested queries etc, cache specific resources with specific time constraints. It’s extremely easy to do all of this.
With GraphQL, there is no rate limiting, and there are NO joins by default. The N+1 problem makes the performance hideous.
TLDR: The N+1 problem would lead to a SINGLE database request for each association. In the above example, instead of using a JOIN (or even 2 statements in SQL), the GET call for books for a given author will look like:
SELECT * from Books where authorId = author1;
SELECT * from Books where authorId = author2;
...
and the GraphQL layer will do the data formatting.
But to the team’s credit, the N+1 problem has largely been solved, using Data Loaders.
In a nutshell, even though the declared query makes it look like a single query, the number of database queries can grow vastly with the size of your data. Other than Hasura, I have yet to see an efficient query parsing solution for GraphQL.
When GraphQL would be a good fit
Stepping back, and ignoring my rant for a minute, I do think GraphQL is a wonderful tool for specific use cases.
1. Scaling isn’t a big problem for your backend.
If you don’t expect a super high traffic system, GraphQL can vastly simplify your codebase. An app that is used to find electricians in your local area isn’t going to have nearly the same traffic as a food delivery app. Analyze your use case, and pick the right tool.
2. Authorization is simplistic.
If you don’t need the fine-grained control of ABAC, and a simplistic RBAC model will cover your needs, GraphQL might be a good enough fit. In fact, in certain cases, it might make your code a LOT more readable. I find this much easier to understand than any other REST auth model I’ve seen till date.
Onwards & Upwards!
Having spent quite a few months working on (and learning) GraphQL, I do realize it’s potential. It’s backed by an awesome community, has a growing base, and these teams are making a LOT of progress on the problems mentioned above. For example, fastQL is a GraphQL only caching service that is trying to solve the above mentioned caching problem.
As time progresses, and GraphQL catches up to REST, I hope to see more and more teams adopting this technology, and helping the tech world progress!