I'll be honest. I hate GraphQL. It probably makes sense in a world where everyone uses graphQL, but to me it felt like having to learn yet another query language to do what to me seems straightforward using a simple REST api.
I may also be old and cranky and you should probably get off of my lawn.
I’m 100% with you. We’ve implemented a GraphQL gateway to our REST APIs at my company, and IMO it’s been a tremendous waste of time. Tonnes of complexity, performance issues, time writing the server, monitoring problems when calls are no longer to simple endpoints, etc., for almost no tangible gain.
Also, a much more minor issue, but when everything is a POST to a single endpoint, debugging network calls in Chrome/whatever dev tools is more of a pain in the ass. It’s a lot easy browsing through GET /users/124, DELETE /messages/456, etc., and instantly see what’s happening, than having every call be POST /graphql, and have to read through all the giant post bodies to figure out what’s going on.
IMO GraphQL is no better than all the other multitude of RPC frameworks that everyone eventually realizes are a snake pits of unnecessary complexity when compared to REST. It’s just newer, so people don’t hate it as much YET.
With an API that keeps tighter control over access patterns, you've got a more predictable target for optimizing your indexing strategy. With GraphQL, you've got to worry about the possibility that some client figures out how to craft a query that slips between all your indexes and causes the database engine to resort to doing things the hard way. So, worrying about that stuff is hard, where it tends to be easier to manage with REST or gRPC because you can just force your worldview onto consumers and get on with your life.
In theory, though, a well-crafted GraphQL API can be more performant over a wider variety of use cases than a well-crafted REST API, for all the oft-cited reasons. So it does make the impossible possible. But not (necessarily) easy.
However, to be super efficient you need to give up on some consistency. You simply can't have data points which join directly in the db. Instead, you need to make separate parallel requests for those datapoints and let the dataloader be in charge of merging them into larger batches of requests for the db to fullfill.
This can result in some additional latency on a request, but ultimately provides the best way to be able to scale things out.
The benefit of rest is that it's easier to make a really fast on single request endpoint. You can precisely tune your db indexes to match your queries. For graphql to be fast and not kill your DB with a malicious query, you need to introduce that wait time/batching.
Sure, but that's a great example of what I'm getting at. All that complexity may let you do some pretty cool things. But it also comes at a cost, in terms of both development effort and implementation complexity, and I certainly wouldn't call it easy.
I fear, sometimes, that our collective tendency to prefer talking about the most interesting or most capable technologies tends to bias us toward over-engineering. Slinging JSON over HTTP is, in my personal opinion, a pretty hokey hack. But it's also the option that's the easiest to implement, the most widely understood, and, more often than not, it's more than up to the task.
> Instead, you need to make separate parallel requests for those datapoints and let the dataloader be in charge of merging them into larger batches of requests for the db to fullfill.
> This can result in some additional latency on a request, but ultimately provides the best way to be able to scale things out.
I promise you, this is not the best way to scale things out.
The thing that I usually do is work tightly with the frontend and extract out all queries that I would be sending to my backend and whitelist only those.
F.e. when we knew that we are going to have a big spike because of a feature in the news, we checked the cost of our queries and heavily optimized and added a cache just for those queries that are costing us the most in front of our backend (based on the query string). This enabled us scaling up from 2000 concurrent users to half a million (the difference is only that big because we were super badly unoptimized before and also the near infinite limit of Cloudflare workers)
It's definitely harder when you have bigger different teams interacting with a single central GraphQL api. My rule for that is that there needs to be a gateway that handles exactly that for every service/team/whatever. Not custom coded by every team because this 100% gets mismanaged. Instead it should be just a container image managed by the same team that handles the GraphQL api and configured by the consuming team via an env var or a file containing all the queries needed.
I handle it w/ GraphQL the same way I do with the include query param on a rest endpoint, there's tool to make parsing the query into necessary fields easy.
No, but I realize it sounded like a typo. Perhaps, in retrospect, a cleverer wording would have been, "makes impossible things possible and easy things. . . possible."
Perhaps that's my bias showing. Lately I've mostly been working in Java, a context where the words "easy" and "Web" rarely belong together in the same sentence.
> "makes impossible things possible and easy things. . . possible."
I like that one! Yeah, I totally thought it was a typo, but I do think that GraphQL makes very complicated things easy. I might also be biased because I've been working with it for a while now.
Also the API provider had a "GraphQL is self-descriptive, go away" attitude when asked for documentation that made things worse.
It could be me (or my team), but we didn't find it easy at all to explore the API and find what we were looking for. We ended using a Python tool that generated some classes from the schema and, thanks to that, we managed to figure out the queries we needed to use in our Scala client. Not a fan.
There are definitely some cons, but I don't think the learning curve of the query language is one of them. It's simple enough that you can easily pick it up after 15 mins of reading the docs. There is also a schema proved by every server that tells you exactly what can be queried. This is much nicer than having to refer to documentation of unknown quality before you know what a REST api can provide.
Now actually implementing a server on the other hand is much more difficult.
Yeah, it just that with the use case I was exposed to (payment processing) it seemed utterly pointless and overly complex when compared to the rest API.
I don't want to query. I want to submit a transaction for processing dammit.
Again though, it might just be a case of me being old and cranky and having to learn yet another query language.
I introduced graphql a few months ago to basically unblock myself from having to think about and design gazillions of custom REST endpoints for our mobile client developers. Turns out, that I don't miss doing that. REST has been a huge drain intellectually on this industry ever since people got pedantic over Roy Fielding's thesis and insisted that we stop treating HTTP like yet another RPC mechanism. The amount of debates I've been involved in over such arcane detail as the virtues of using a PUT vs POST and exactly which is more 'correct' in what situation is beyond ridiculous. I appreciate a well designed REST API as much as anyone but for most projects where the frontend code is the single customer of the API, it's not very relevant. If you are shipping SDKs to third parties, it's a different matter of course.
In any case, we now have the graphql playground where you can prototype your queries with full autocomplete (based on the schema). I've done this with third party graphql APIs; it's stupidly easy and you don't need a lot of documentation generally.
We're using the Expedia implementation for Kotlin and Spring Boot. I have a suspicion that that setup might be lot easier to deal with than Appollo and node.js since it has the important feature of using reflection for creating the schema from code. I've not written a single line of graphql schema in nearly 6 months of creating dozens of graphql endpoints. We also use kotlinx serialization to generate cross platform parsing code in our multiplatform client (we use it on Android and in the browser and soon on IOS). So, this offloads a lot of hassle of dealing with schemas and parsing both client and server side that we used to have with REST based APIs. Maybe not the most common path but worth checking out if you are looking to get started with this stuff.
My process for adding a new endpoint:
1) write a function in a spring bean that implements the Mutation or Query marker interface. Spring Boot does the rest. It generates the schema at startup time and wires everything together.
2) start a dev server, prototype the new graphql query in the playground
3) paste the working query to a new function with a multi line string along with any model classes we need in our multiplatform (js, android, and soon ios native) client library and recompile that to add the new client code for the query.
4) update the dependency on our android and web projects (we use kotlin-js for our admin UI) to use it.
5) also add the new client to our integration test project so we can write some tests for the new endpoint. We have full end to end tests of our client and API. Our server uses some mocked databases and middleware when running the tests.
It's definitely not perfect; the Expedia implementation definitely has some quirks and limitations. Also, Kotlin multiplatform has been a bit of a moving target in the last few months (though a lot more usable as of Kotlin 1.4.x). But overall it's a great setup for a small team that has better things to do than crafting custom REST APIs.
In terms of performance, technically graphql is just an HTTP POST API on top of Spring Boot (for us at least). Yes, there's a bit of overhead for query processing on the server but most of your performance is otherwise exactly the same as it would otherwise be. You of course pay a price for crafting complicated queries. But that's the same price you pay for having poorly aligned UI and REST APIs where you end up making lots of REST calls because you did not design your API right (been there, done that). Graphql just allows you to iterate on that more easily. But it's not inherently slower in any way. We are currently not doing any federation but that's mostly because we have a monolith server instead of micro-services.
I'm with you. I've done big REST APIs and now have a big GraphQL API on my ongoing project, and I wouldn't do GraphQL again for anything I'm working on. The beneficial use cases for GraphQL are far narrower than presented, and the extra overhead compared to REST isn't worth it.
Same. But I'm also getting old and cranky. Every advantage typically pointed out over REST could easily be solved in REST. You can do joins in REST people, don't be afraid! I often would add query params for such common things, such as (fake example) fillChildren=true to have what is essentially a parent object populated with its child object in what would normally be separate calls.
Of course you can do that. But eventually keeping up with all the different fill parameters is going to catch up to you.
Besides, what do you call the parameter to fill the owner of the children? Fillchildrenowners? It’s nicer to work with if your API takes this into account.
At work almost every endpoint supports an “expand” parameter that will do various expansions of referenced resources in the returned records. This can:
- cause additional sql joins
- or pull individual records from a cache (if it’s a small enough dataset)
- or cause one additional DB query and save a network round trip.
There is one huge advantage that i don't believe REST can solve: a GraphQL server returns a schema that tells you exactly what can be queried. When using REST endpoints you are at the mercy an API's documentation, which is often quite poor. I know there are tools like Swagger that solve this problem to some extent, but its not baked into the standard like with GraphQL.
Of course you can build these elements into your own implementation, but there is value in a higher level standard that has these items guaranteed. If a product advertise a GraphQL api you know immediately there will be no trouble with determining what the api can return and accept.
> Of course you can build these elements into your own implementation, but there is value in a higher level standard that has these items guaranteed.
That's what OpenAPI is. (You mention the Swagger tool, but claim it's not baked into “the standard” the way GraphQL is; as well as a tool, Swagger was also the name of the standard, though the current version of that standard is called OpenAPI.)
OData (which is REST) can return the schema and much more: $metadata resource describes all entities, operations, relations, and can also contain documentation, capabilities, etc.
I am pretty sure there's something about REST that makes it so that it's easy to discover what resources are available.
I just pulled up Fielding's paper and couldn't find anything, but wikipedia has a reference to 'Hypermedia as the engine of application state' (HATEOAS).
But then again, I am not sure anyone actually writes systems like this. Very few people actually write REST systems and instead make REST-like APIs.
That's a good point as API doc is often very, very wrong. Still, this could just as easily be added as some semi standardized REST extension/spec. Granted, it wouldn't have the traction up front GQL gives you.
True, and this also something OData has as part of the standard. Using a standard way to do this has big benefits, as this knowledge can be implemented in tools (BI, ETL, low-code dev), which can then support many REST endpoints.
We use jsonrpc over ws on f/e-b/e and between b/e-b/e services in trading system, typescript types for api, runtime type assertion combinator library for io boundary type checks, backend teams co-maintain client libraries to access the service, it works very fast, it is safe and easy to maintain/track changes etc.
JSON-RPC is my protocol of choice as well. I feel most of these other protocols are mostly an exercise in information exchange theory which make them too idealistic, resulting in poor implementations that do not follow the standard or are extended in non-conforming ways.
In the end you simply want to interact with the client or server and procedure calls do just that. I honestly do not see the use in over complicating that.
I've used jsonsprc a few times and my experience was excellent (opposite to GraphQL, actually).
But the jsonrpc endpoint had good documentation and the client library didn't feel alien to the project, like a big query string embedded in the client code.
I wonder why jsonrpc is not used more often; but I guess compared to a REST API, the client may be more complicated.
You write the moral equivelent of __attribute__((graphql)) on your code, and boom, you can query it. You want mutations? __attribute__((graphql_root_mutation)). If your object is stored in TAO, everything works perfect. You can add custom fields or even custom objects implemented in PHP that can do whatever the hell they want.
You never have to think about a database. And you barely even have to think about security if you're using pre-existing objects, the rules for which users should be allowed to see which objects are written in one centralized place and enforced everywhere in the codebase, graphql included.
Of course, it only works that well because there are multiple ~20-30 person teams maintaining that infrastructure. And GraphQL was designed with Facebook's infrastructure in mind.
Outside of Facebook, I cannot see myself using GraphQL for any reason.
I may also be old and cranky and you should probably get off of my lawn.