Our application helps BigCos to understand if they pay people fairly and to run smart pay reviews. It's a relatively small codebase, ~100k LOC, but it's essential complexity is in managing and connecting dispersed data about employees and markets. GraphQL allows us to represent the natural links within this data, then the app frontends can present whatever business information is helpful in that page/sidebar/widget/card without separate endpoints.
With REST, we had the same problems with every feature: over/under-fetching, can't express relationships well, and can't evolve the schema easily. When we tried to work around these issues (e.g. "v2?fields=a,b,c"), we ended up with a poorly implemented subsection of GraphQL that's not benefiting from Facebook's experience. To compare to the world of databases, I view REST as a Key-Value protocol and GraphQL as an SQL with joins and functions. If all you need is to lookup a document, don't overcomplicate it. But if you need to express relations, you don't want to do that in userland.
The only advantage of REST is using a widely known standard with rich tooling and well-published "best practices" (that just try to work around REST limitations).
GET /documents
GET /documents/1/comments
GET /documents/2/comments
GET /documents/3/comments
GET /documents/4/comments
GET /documents/5/comments
..
GET /documents/99/comments
GET /documents/100/comments
Easy, right?Think of how this can be done on a web page. The documents page has a link to follow, "include comments", which links to "?include=comments". The exact query doesn't matter, what's important is that a client can discover new information without needing any out-of-band information.
The next natural step will be - "Thanks for the comments, now the user needs to sort them by votes/date, ok?" and the URL starts looking like a query anyway, so you're on the way of re-implementing GraphQL. Then do you send avatars/timestamps/changeflags for all comments, or only on main body in this endpoint? Now you have the over/under-fetching problems.
The step after that will be "Oh there are 300 of them - I just want the top 3 comments first, and a pager for the rest ok?" and you simply can't do that with the structure REST mandates.
There is no structure that REST mandates, if there were it would be a specification and not an architectural style. There is nothing that says that you can't query for that:
GET /comments?sort=votes&limit=3
The exact string doesn't matter, what matters is the client can discover this. How HTML does this is by generating query parameters based on form inputs.
Given a trivial question like "here's a user, I need her friends and their countries of birth" and the REST answer is a separate endpoint or an O(n) operation on the client because these are _separate resources_. You want the country flag too? I'm sorry, I can't support that requirement.
With GraphQL, it's -
user(handle: "daliwali") {
name,
friends {
country {
name, population, flag
}
}
}
No separate endpoint. You want to show to the client how many dogs those friends have, and if any of them play frisbee? No problems, and no backend engineers involved.This is why I said REST is a key-value protocol like in redis. With redis you can either embed a small objects (which is not a relationship) or keep a PK of the relationships (which means many queries). The more consumers your API has, and the higher the latency, the more expensive either of those choices becomes.
Where you are wrong is that REST doesn't mandate that a resource can only be accessed by its "primary key". There is nothing stopping me from requesting the following URL:
GET /users/daliwali?fields=name&include=friends,friends.country
Any client would be able to follow that link and get something out of it, whether it's JSON or HTML, without needing specialized tooling such as a GraphQL client. A hyperlink makes that query widely accessible and interoperable with any HTTP client.
POST /queries
With some raw database query as the payload (please don't actually do this) and the server could respond with HTTP 202 Accepted, meaning that it's going to take some time to process, meanwhile check back at the URL in the Location header when it's finished.
REST does not mandate any upper bound on complexity, that's up to you to decide.
With web pages, it's standard and effortlessly handled by the browser. Given a form with inputs specified by the server, a client sends a request with media type application/x-www-form-urlencoded or multipart/form-data. There is a vast number of implementations for practically every language and platform. A machine client can send a form too, or JSON for that matter.
You have it backwards, Facebook is attempting to rewrite web standards by themselves. And that is undermining the open web.
What I believe others are getting at is that those complicated queries themselves need to be expressed somehow even within a REST request. GraphQL provides a convenient mechanism for doing so.
In theory you can even do GraphQL RESTfully, with each REST endpoint exposing its own schema. But in doing so it quickly becomes apparent that it would be easier just to forego REST conventions and expose a single consolidated schema.
Speaking for my own GraphQL experiences, on one project we had a GraphQL query generating reports that was around 50 lines long, with half a dozen fragments. Flattened out to match the approaches you've suggested for POST data and GET params it would have been hundreds of attributes. It's unclear that there's any open standard that would have provided as simple of a solution for expressing and executing those requests.
http://myapi/graphql?query={me{name}}
http://graphql.org/learn/serving-over-http/#get-requestActually, I think that the answer following the REST architectural style, using HTTP, and not resorting to a custom HTTP method (which, actually, consistent with the REST architectural style would arguably be justified in this case), the correct approach would be to POST a representation of a resource representing the query to an appropriate query endpoint (which could be the user endpoint for queries specific to that user, since the query could reasonably be seen as a subordinate entity to the user to which it applied), and then GET the result of that query.
(In fact, the given GraphQL could well be the representation of the query.)
Ideally, HTTP needs a generalized safe method (like GET) that takes a payload (like POST) named something like SEARCH (there are HTTP-based technologies with domain-specific versions of this, but no generally accepted representation-neutral version) so that this can be, when the server architecture supports synchronous exchanges, a one-round-trip process.
> This is why I said REST is a key-value protocol like in redis.
But this is simply false. Its trivial to make key-value protocols that follow REST (especially the REST-minus-HATEOAS that is common these days), but REST isn't limited to that, or even specialized to it.