In my opinion, GraphQL moves too much of the burden to the user of the API. It makes most sense if the data is highly dynamic, you have a mobile app and every call is expensive, or (and this seems more common) the backend and frontend teams don't like to talk to each other. As a user, I just want to GET /foo, with a good old API token I pasted from your dev docs, and move on to the next thing. I don't want to spend time figuring out your database schema. Or perhaps I've just yet to see single good public GraphQL API. I just recently had look at the Github GraphQL API and it's non-existent docs (no, I don't want introspect your data models at runtime), noped the hell out of that, and got the REST integration for the bit I needed done in an hour.
They were no doubt frustrated with what they thought was a needless hassle compared to REST, and I myself found a lot of the query building pretty tedious.
I agree with query building being tedious, though I'm working on a query builder helper library to make this less painful. Hopefully at some point I can open source it.
This is the bit of abstraction I see most developers fail to understand when evaluating GraphQL.
If by OP you mean the original article, I completely agree with you; my comment was directed to its ancestors.
But I actually found their graphql docs to be sensible? Not sure where the introspection comes into play
How I _wish_ I could nope the hell out of our GraphQL dependency, for the reasons listed and for the frighteningly complex client libs we have to use to consume it, rather than straight up ajax/fetch.
The state management is a problem by default. We wrote our own cache driver to fix it, which is pretty well documented thankfully. "Un-magic it" is a phrase we use a lot.
That's obviously not the same formula for every company. I'm just offering it as a potential counterpoint.
PM: Make this change to the site
Backend: that would take 2 weeks to make sure it doesn't break anything
Frontend: That's a tiny change! I can do it in 2 hours
Frontend makes the change. Along with the next several. Overtime, the backend team uses the lessened pressure to overhaul/replace/refactor to decrease the complexity. Meanwhile, Frontend is starting to accumulate a collection of unrelated exceptions to the "normal" flow to meet demands. Eventually....
Frontend: Um...that would risk all sorts of complications. Consider it a few weeks to make sure it doesn't break anything
Backend: That's a straightforward change, I can do it in 2 hours
And the pendulum starts to swing in the opposite direction...
Where the GraphQL burden lies is largely due to who is considered to be its product owner. This varies significantly depending on whether it's an API for first or third parties.
First of all, our backend is extremely complex and our frontend isn't. Our backend devs need a lot of legal and financial training. Other companies will have an inverse situation.
Second, there's a glut of frontend devs (at least when I look for them) because bootcamps seem to produce people who are better at frontend than backend.
And third, our frontend devs just don't have as much work to do, so they're idle a lot more often.
I just found it a surprising comment in isolation because most people I’ve met who’ve developed both frontend and backend skills to a senior or greater level, would argue that frontend is the more challenging of the two.
The bit about idle time is a bit confusing though, can’t you just have fewer devs if there’s not enough work to go around? Or do you always need a lot of spare resources due to unpredictable workloads?
We’ve also found that on boarding our new hires is much simpler. There’s a lot of misinformation about REST, and we were having to retrain people, and when they wanted to see our schema we would then have to teach them swagger as well. With GraphQL we just send them to the official docs with our schema with is our single source of truth for the API and they come back a day later ready to go. Generally GraphQL being more standardized and centrally managed has been great from a training perspective.
I mean, you could have also just done this with what you had.
You could just take your GraphQL backend and implement those specialized REST calls, sure, but then why not expose it?
There's no such thing.
> you have to name all such combinations in advance.
No you don't.
> Possibly build custom code for each.
As much as any other API.
... OData ...
It's a thing.
The burden of dictating specs is just shifted around, and it seems the workload on the server side is bigger with h GraphQL (wider range of cases to handle).
Am I missing something ?
> REST necessitates multiple queries
This is a self imposed limitation at best
You still have to do the data mapping, fetching everything from DB in a reasonable way, and make sure it all makes sense performance wise.
All of that will be needed for any API, but it seems to me GraphQL adds the uncertainty on how much data will be exchanged (lots of small queries ? a few big queries ?), what can be optimized, how will cache behave etc.
I remember a study on te github API on how some types of queries would make it crawl excessively. It fear it becomes a nightmare to try to cover for all the cases that can go wrong.
I don't know what your use case is, but it sounds like you should just try it, I can only say that for us (multiple FE experiences, pushing logic to backend), it's been very good. If you have a different usecase, ymmv.
It was this study:
Overall it acknowledges GraphQL's advantages, while warning that the data retrieval part shouldn't be done naively.
> We note that this issue is somehow acknowledged by the Github GraphQL interface and, as a safety measure to avoid queries that might turn out to be too resource-intensive, it introduces a few syntactic restrictions . As one such restriction, Github imposes a maximum level of nesting for queries that it accepts for execution.
However, even with this restriction (and other syntactic restric- tions imposed by the Github GraphQL interface ), Github fails to avoid all queries that hit some resource limits when executed
> Also REST necessitates multiple HTTP queries to fetch multiple resources.
Again, says who?
I don't want this to come off as a personal attack (and I apologize if it does), but your comment contains absolutely no information whatsoever regarding a specific situation/use-case, nothing from which the rest of us can formulate our own opinions on the REST/GraphQL discussion.
>it solved a lot of the administrative and philosophical headaches
>considerably reduced the number of connections
>considerably reduced wasted data
>made our client code so much simpler through easily grokked queries
I feel that grandparent does contain information which might be valuable for adoption.
In the end I would guess you can end up in a similar place with a standard fetch-json design if you just ignore ReST dogma and focus on getting the API into a shape that fits the need.
Not saying ReST dogma is necessarily wrong, or bad, just that it’s easy to get lost in design when focusing more on learning others design than understanding the actual problem you’re trying to solve.
There was this long pause and then we both just started laughing.
File under: shit that doesn't happen any more because nobody plays single-player games side-by-side, late at night any longer.
PC police might be on to terms like this nowadays, but by using the term I understood them to mean:
- A less loved resource
- A potentially ugly resource
- A resource that causes trouble
This sounds a little over-the-top. There are certainly cases where REST would be the better recommendation over GraphQL. I have no idea what your specific requirements were but if building a REST API was a 'monumental effort' then GraphQL was probably a good choice for you. That does not mean that in all cases GraphQL > REST.
REST semantics are a distraction. The best possible outcome of REST is when developers are encouraged to consider the concept of "idempotency" when they stumble upon the technical definition of the "PUT" verb. Everything else is line noise.
GraphQL has a schema with types. It makes it very straightforward and approachable for all developers to reason about what an API should deliver up-front, and also easy to reason about what is and is not a breaking change. Automatic validation of queries against the schema also saves massive amounts of time in writing validation logic.
There are still things that could be better with GraphQL. The query language is... interesting. The whole thing is still very client-server asymmetric -- look at a client query syntax versus the schema syntax you'll use on the server side for three seconds and you'll immediately and viscerally know what I mean -- and that strikes me as disappointing in this age. It's still very easy for developers to fall into mental quicksand which causes them to make many individual requests even when GraphQL would let them batch things up into one. And so on.
But overall: yes, working on a GraphQL stack is an awesome experience compared to going it alone with REST and JSON and crossed fingers.
It's always weird to see this type of thing on HN. How anyone can call the way the web works "line noise" is baffling to me.
A good summary
I love how flexible it is for client developers and because the great client side libraries it helps to eliminate a ton of boiler plate code on the client side.
My biggest complaint has been "lost" exceptions and caching.
Because it's possible for an exception to be thrown server side on one field while the other ones succeed I've been plagued with hard to monitor/find errors. I ended up writing a shim to parse the response in an attempt to get more insight into #errors / fields (this has also been really helpful for monitoring slow queries in new relic since all requests go to the same endpoint which breaks a ton of APM monitoring).
My other issue has been around caching, in apollo there are ways to say "don't use the cache for this request", but it's not to give an object a cache ttl. My app allows users to search for events that are happening near them right now, and I've run into several issues where apollo decided that an event from yesterday should be added to a result. It happened frequently enough even with queries that included times as an argument that I ended up basically implementing a "middleware" between what apollo gives back and the component, which felt really ugly.
for the error policy, you basically control the behavior (for each client instance or each request) when a request is considered failed. none, ignore and all. 
fetch policy allows you immense control over caching. this all depends on what you're doing but in some instances it can even make sense to never cache any requests, depending on how you application is structured. the docs are hard to google for this, but here's a link for you 
It's just really annoying that I have a ton of queries that return lists of objects, where each object has an easily known expiration time, and there's no way to say, "cache this result but remove the elements that have expired". I can't even say, "cache this list until X time", which would be very easy in a RESTful environment. I know there are extensions to support caching but none of them are universal yet. I'm hopeful that the community will land on an answer for object level cache expiration.
This is quite annoying because the server never throws a 500 error so the normal logging doens't kick in.
Trying to fit it into an existing ORM paradigm with respect to complex sub-collections, lazy loading, and efficient database querying, it just didn’t work out for us.
Disclaimer: I work at MDG on Apollo Engine (the backend of which is Kotlin utilising graphql-java).
Btw I'm really liking apollo.
These last months, I've been working non-stop in a blazing-fast GraphQL engine that beats the rest by an order of magnitude - https://graphql-quiver.com/
As a frontend dev, I had a positive experience with one service because the backend was far more willing to add new query options. The much publicized "only get what you ask for" part was largely irrelevant.
I am, however, unsettled at the prospect is losing all the built in network and browser caching for idempotent calls (mostly I'm unsettled because no one else seems to seriously consider the issue - it may end up too small to matter, but I dont trust that anyone else here has honestly evaluated it).
Another poster mentioned the issue with partial errors, which sounds like something else that will not get the upfront attention it deserves, while not being an immediate dealbreaker. Add in to that how to manage deprecation of particular query statements as they can no longer be distinguished as distinct endpoints.
My other concern is how much magic frontend libraries provide. This magic looks great if your app is nothing more than input/output over CRUD calls, but sounds very brittle if your app has client side logic (and while perhaps a webapp should ideally avoid that, other services can also be clients.)
So far I have concerns but not concrete problems, I just worry that we wont be able to confirm the severity until we've already invested and committed, particularly when our initial adopters are so enthusiastic. At the same time I don't want to be the guy unwilling to change and adopt new things.
It went well, it greatly simplified both our backend and frontend code. The backend code got simpler and more stable because it no longer had to deal with "data packaging". The frontend code became more transparent, because you can now easily read what data gets exchanged, and more decoupled, because different components can independently require the data they care about. One of the biggest benefits has been the degree of independence to evolve both the client and server.
We haven't had the performance issues some people have mentioned (N+1 query etc.), because of the server-side design of our homemade GraphQL engine - in which data resolvers are batching and asynchronous by default, unlike most backend libs which approach this problem more naively. Will open-source that soon.
The biggest limitations I see to GraphQL are:
- it doesn't really have a story for caching. I have some ideas for addressing that, but it hasn't been a problem for us really.
- it repeated the SQL mistake of exposing a query language based on text, not data structures. Now we have to write queries as templates instead of assembling them programmatically. This hurts both application developers and library authors.
- it doesn't really have a story for structured writes.
Out of curiosity couldn't you just treat queries as a composable AST and "compile" the query text from that?
You could write such a wrapper, but it won't be standard, which means that there will never be a robust library ecosystem and set of practices that arises from that. If GraphQL was to evolve, you also wouldn't be sure your wrapper can express all of GraphQL. Finally, this would add new practical hurdles, like a new dependency, additional runtime cost of compilation, obscured source mapping, etc.
Meanwhile, because GraphQL is just a text-based DSL, most people don't have a clear idea of the _information model_ of GraphQL queries - you have to read the spec to get this idea. If we are to build a robust ecosystem on top of GraphQL, this information model should be evident in the API.
All these problems are present, and very damaging, in SQL.
I don't think it is either of. I use both. In the same API. The two are largely compatible.
All REST APIs can be modeled in RPC style APIs and that's no different with GraphQL.
I've done APIs where I have a GraphQL facade in front of REST and REST Facades infront of GraphQL by having all my REST endpoints be two lines
1. Graph QL query
2. Format result as REST
That first like maps to a graphQL query and the second one can be standardized for all your REST endpoints.
I tend to like REST in-front of GraphQL better since it allows for some performance optimizations when you know ahead of time all the data you need to grab.
And since GraphQL can be mapped to classes you could also just skip the GraphQL query compile and use the classes directly from your REST with only a few more lines of code.
Overall I like GraphQL a lot because it allows the frontend to make less round trips to the server and makes it easier to exclude data you don't want (which helps when data is large). Json:API tries to solve some of this with includes, related, and fields but it doesn't quite allow as much expressiveness as GraphQL.
Out of curiosity, how do you secure your public-facing APIs and how do you authenticate/rate-limit users?
I don't centrally track rates unless a signature comes close to 1/N the limit where N is the number of nodes. At which point I will talk to the other nodes Peer to Peer.
Can still be abused but works pretty well most of the time. It also doesn't work if you have a number of nodes that is approximating your rate limit because if you do, you hit 1/N on request #1.
For that reason I tend to choose pretty lenient rate limits (call it one request a second with bursts in a 5 minute window)
For write I use OAuth2 with bearer tokens being a JWT token with a short expiry. I only need to maintain a blacklist of invalidated tokens for the length of the expiry. Rate limiting would work the same way as reads.
We were mid-way through our project before realizing GraphQL might be a better fit for our use case so we paused for a week to play around with it and see if we could stand up something inside of our project that made sense.
GraphQL felt very "plug and play" to us. Aside from having to re-work some validation to fit into GraphQL's idea of mutations, we were mostly able to drop our existing models and logic directly in and see it working right away.
Having built very well defined REST APIs (and SOAP before that) for years, the flexibility that GraphQL offers made me feel a bit "uneasy" at first but I have come around to appreciating how much freedom it gives the front-end to only request the data they need.
I'm usually the type to shy away from flashy new doodads and stick with what I know is safe+reliable, especially in an enterprise environment, but as the project continues I'm feeling more and more confident in our choice. I suppose only time will tell though.
There is an advantage to making technology decisions behind-the-curve, choosing mature, "battle-tested" technology, even if it means overlooking some known warts, and missing out on what's "hot". We can call this being a "late-adopter". I am most interested in N-years-later perspectives where N is around 3 or more.
* As a consumer of APIs I vastly prefer REST APIs.
* Highly recommend GraphQL to anyone.
* I *love* how flexible it is for client developers.
* As a manager/business owner, I like GraphQL.
* How I _wish_ I could nope the hell out of our GraphQL dependency
* Unnecessary complexity over simple rest calls with no benefits.
* REST semantics are a distraction
* my experience is just ok.
Just because it worked for me doesn't mean it will work great for everyone else; and if it sucks for me doesn't mean it won't be a life-saver for someone else. Those are just tools; use them as you find fit.
FYI we're using it in production over a django backend (which comes with some drawbacks, since subscriptions == pushed updates are not perfectly implemented) with our react/apollo apps (web and native) and in my opinion the overhead lies surprisingly more in the frontend side (writing data connectors is longer, but way more explicit, than using rest queries returning json)than on the backend (where you just declare resolvers, a thing you don't even need to do in nodejs) and handle permissions.
This is a piece of GraphQL I haven't been able to get my head around. Could you elaborate or point me to a good explanation of how this is implemented? Everything I found when I looked into GraphQL previously was something like "you control access to individual resources in your business layer" but never explained how.
Inside that function you can write any code you’d like, including permissions code.
* Watch out for bad implementation of the GraphQL API (this will definitely result in bad performance).
* Design the GraphQL schema that you want the user to see/perceive. Not every object or field in your database needs to be exposed via the API the way it is.
My workplace is currently moving a huge monolith into a bunch of manageable components. Each of these components has its own GraphQL endpoint. Using schema-stitching, these are being stitched together into one endpoint for API users.
As a result of our codebase, we've tried GraphQL in:
* Ruby (graphql-ruby) - WATCHOUT Relay arguments for connection fields are not exposed to the library user. So basically you have to implement your own Relay-compliant stuff if you need access to the pagination arguments from Relay. Also, documentation is broken.
* Python (graphene) - We've had no issues so far. We worked around it.
* Node.js (Apollo GraphQL) - OH MY BUTTERFLIES. So far, this is the ONLY library I have come across that is polished and has plenty of documentation.
* Elixir (Absinthe) - My coworker worked on this part. He did not complain. So I'm assuming he had no issues.
The "Learn * in a day" joke applies to GraphQL. As simple as GraphQL looks for the client-side, it is beast of a job to build a GraphQL backend that is optimized for production.
Servers-side implementation of GraphQL is not very well documented apart from hello-worldly examples. Most of the knowledge found online is about client-side usage.
Due to poor documentation/examples provided, ramping up people with GraphQL is hard. Most first iterations I've had to review were slower than our REST APIs because of unoptimized code. Sitting down for a few minutes solves that problem.
To ramp up people at work place, I ended up having to do this:
* Ask people to use the GitHub v4 API to checkout GraphQL.
* Make them build a GraphQL server for a blog app.
* Dive straight into whatever feature/API they would build.
* Review their work a few dozen times and show them optimization tricks.
My most valuable lesson: When in doubt, dig into the source of these libraries.
if you have db schemas on the backend if using orm, get ready to duplicate them again for graphql.
and on the frontend, get prepared to write out every songle fields you need from the backend. i can imagine it may be brutal for those who have a lot of changes in their schemas.
my conclusion is that, since im a fullstack who does both frontend and backend, i feel myself getting a bit more fatigued than when i was doing rest style api. i find myself wanting rest time to time, esp at times i dont feel like writing out all the fields i need back that i cant remember off top of my head.
Wouldn’t you need to do this in some form anyway (since those fields would be displayed or used in some way)?
I would consider this a weakness of ORMs, not of GraphQL.
On the upside, we can construct complex queries than eliminate many consecutive RPCs that you'd end up with in a traditional REST API. At scale this should work wonderfully, greatly reducing the client/server latency for our realtime app.
On the downside, the tooling is still far behind. This is somewhat due to GraphQL being a younger technology so you have to give it some time. OTOH, I feel like you can get things off the ground with REST more quickly. Problems with GraphQL tend to be harder to reconcile due to the debug tooling handicap.
Some of our engineers take a little time ramping up to GraphQL due to its complex nature. This is probably a good thing in the long run though, since it stresses the importance of keeping RPCs to a minimum and eliminates having to sync or batch consecutive RPCs.
Overall I still think it's a win. The tooling should improve over time, and hopefully it will be a first-class citizen in IDEs and libraries soon. Until then, you've got to be prepared to muscle though it.
Current greenfield implements are typically stacked on ORMs like Django and RoR, and the impedance mismatch is real. Personally I abide by the dictum that ORMS are "Vietnam of computer programming" and should be avoided at all costs for anything that will grow beyond a small app. GraphQL was not originally implemented on top of an ORM, but instead an object model built on key-value + edge store internal to Facebook.
In terms of other criticisms in this thread:
1) Exceptions: The default behavior in graphql-js (mimicked in other language implementations) of swallowing native exceptions by default was probably a mistake in hindsight. Whenever I've played with GraphQL using different toolsets the first thing I change is to add a wrapper function which checks for errors and then rethrows the initial exception that caused the GraphQL error for use in testing and CI/CD contexts.
2. Caching: Personally I've always been confused about the concern with leveraging HTTP-level caching. While a clever hack, with any real app with any sort of even mildly dynamic behavior you don't want to do this. Staleness will be interpreted, rightly, as bugs by your users. If you want to replicate the behavior the most straightforward way would be to use persisted queries (described here https://blog.apollographql.com/persisted-graphql-queries-wit...) combined with HTTP GETs. With persisted queries you can encode the entire request in the query string, which should get you the HTTP-level caching you want.
3. Docs: Quite confused about this one. While particular implementations of GraphQL can be problematic the documentation of the core language (which I am not responsible for) is superb. See http://graphql.org/ and https://www.howtographql.com/.
Another issue: there currently are a lot of servers that are REST and it would be really nice if there were ways of incorporating them in the GraphQL schema. One of the most useful features to add here would be ways of linking separate REST objects to each other in the GraphQL schema. This would help adoption quite a bit.
Also; debugging and monitoring GraphQL APIs sucks. Considerations:
- Any subfield of a query can throw an error, but the rest of the fields can succeed, because GraphQL frameworks are allowed to run each field resolver asynchronously.
- Because of this, any GraphQL query is capable of returning multiple errors.
- Rate limiting is exceedingly difficult due to nested resolvers. I've seen solutions which involve annotating your schemas with "cost" numbers, and only allowing each query to run up to a maximum "cost" before failing by dynamically adding the costs of each field they request. Traditional rate limiting doesn't work.
- Traditional APM platforms also don't work. Prepare to adopt Apollo Engine and pay them $600/month on top of the money you're already paying New Relic or Datadog.
The common thread between those is that they're all languages with weak type systems, which is a theme in GraphQL land. Strongly typed languages tend to have usability and safety issues with GraphQL; you end up needing codegen or bypassing type safety to make it work.
There are some quirks (error handling), performance issues (e.g. fixing n+1 queries) and DOS concerns, but again, it isn't all that bad.
(we're using rails/graphql-ruby on backend | react/relay on frontend)
The frontend developers see the biggest improvements, they have one endpoint to worry about and using a library like Apollo makes things really nice.
Overall, I think the improvements are worth it, especially when working on internal APIs.
GraphQL just feels too tied to the datastore on the back end to be generally useful. REST/Swagger is hugely overcomplicated for the basic REST premise of moving objects back and forth.
GRPC is what REST should have been. Ship objects back and forth between multiple languages with minimum fuss.
There's no reason someone who wants an expressive query language like graphQL provides couldn't send that query over gRPC, in which case all the benefits are moot, you can do both. But let the rest of us just make regular RPC calls.
I'm backend Systems Architect at a big publishing company, and my current primary project is an aggregating caching GraphQL proxy for our REST microservices.
Our front ends were making too many calls to the REST APIs, so we went overboard embedding related resources – and now they're getting too much unneeded data back, and cache invalidation is a nightmare. Sounds familiar, probably!
So we're building a GraphQL service that stitches those REST APIs together to let the caller request exactly the fields they need, from any API's resource. By caching individual resources, rather than nested multi-resource serializations, we can invalidate easily by UUID on change events – so cache TTLs can be long – and the GraphQL API's field resolvers can assemble complex responses with a few fast Redis MGETs, which are batched by DataLoaders.
This also gives us a place to centralize business logic, rather than having each front end service reimplement field formatting, resource transforms, &c. Since the REST APIs remain available as the source of truth, existing services can migrate to the GraphQL proxy at their own pace, which we hope will be an easy sell since it's so much faster.
"In GraphQL, every contract is explicit and observable. Providers can use something like a canonical log line to get perfect insight into the fields that are in use for every request, and use that information to make decisions around product development, API changes, and retirement."
You simply cannot know with a REST API what to remove if the decisions on what to return for a request are largely made by the server side team. So, you end up with massive, clunky and confusing responses. GraphQL forces the client side users to understand how to use it, and because of that you get so much more.
I had the chance to participate in moving the client-server architecture from REST to GraphQL when I worked for two companies as a contractor. They didn't regret it so far. There a various advantages (and disadvantages)  using GraphQL.
But these are only the direct impacts of using GraphQL. In the case of these two applications, they had a React client application managed by a GraphQL client library. All the state management done with Redux/MobX was reduced to a minimum, because most of the managed state was remote data from an API. Now the GraphQL client was able to take over, leaving only the local UI state for Redux/MobX. Often it is even possible to remove these state management libraries altogether.
-  https://www.robinwieruch.de/why-graphql-advantages-disadvant...
The main disappointment is that input types are not nearly as expressive as output types.
How does it mutate then? SQL can mutate, I’m not sure why this would be any different. Basically, like SQL’s UPDATE, and INSERT, GQL can have “methods” associated with parts of the schema.
You have resources represented as a graph and you're asking for the state (in some representation -- probably JSON, but maybe protobuf, etc), just in a more expressive and deeper way than a simple HTTP Get with a single URL can express.
Nothing about REST ever said that a resource locator had to be a URL, or that a single resource can't represent a collection of resources.
That's all GraphQL does, IMO. It lets you more flexibly and expressively ask for stat of a collection of resources.
I have been using straight HTTP requests to query the GraphQL endpoint. Now, wanting to do something more complex, I am disappointed in the state of GraphQL clients. Everyone says use Apollo but it has some serious issues (e.g. caching ). Relay seems over-kill/restrictive and none of the other alternatives seem complete. I feel like maybe my life will be a bit easier if I just stick with REST especially since I am proxying an exiting API.
2. Document database
I know there are things that do two of these. I want all three.
It's great but it's not quite perfect for me: I'd prefer if it produced a function that accepts your query executor and inputs, and returns an observable of the result, and it doesn't work very smoothly with angular (especially 5+).
Sometimes 1 and 3 (see http://avant.engineering/graphql-and-typescript/)
That being said, there are cases where a join between ad hoc subqueries is the best way, and GraphQL doesn't really offer a way to do that (though I don't see why it wouldn't be possible). E.g. arbitrarily combining two GraphQL queries that return lists where some field in one is equal to some field in another.
But in terms of replacing REST, where you have to do all of that anyway, it is far and away the better option (for ad hoc querying, at least).
I think you're supposed to create a "virtual" field on the left-hand object that represents a collection of the right-hand type of objects. The field can be parameterized if your join needs extra information. If you want pagination, the virtual field returns an intermediary object describing the cursor (sort order, offset).
We have a Django app on the backend and have used Django REST Framework for our REST API. That was a great experience, but had some limitations. We've written our new GraphQL API with the help of Graphene and it has been awesome. Everything is very declarative and exploring the API schema couldn't be easier using GraphiQL.
Implementing it on the frontend has been great too. Yes, you do have to specify what fields you want, but having complete control is worth the extra boilerplate. I'd recommend it heartily.
The client libraries are too complex, there's too much magic going on, debugging is a pain, there's not a lot of native support in browsers and mobile devices. I don't want to step into the code of a client library I didn't write to figure out some simple query issue. It's no longer a simple matter of just an HTTP request anymore. It's not a pragmatic choice.
So I guess my question is, why would I use GraphQL over, say, the Swagger tool suite? Swagger and the OpenAPI spec defines a way of doing REST that best fits both what Roy Fielding meant for REST but also fits IDE and tool automation systems.
There are also a ton of maintenance benefits, like for example if you add a field in GraphQL that's perfectly fine to not change the version because that break any existing calls, which is not always true with REST.
That's not a unique benefit of GraphQL. Any HTTP based API can do this, regardless of how it's designed.
Some specifications even have it baked in (See: jsonapi.org).
> GraphQL from a front end or mobile POV makes your api more like a data store that it can interact with and query for it's needs, which makes app UI work much nicer.
Again, not unique to GraphQL.
> There are also a ton of maintenance benefits, like for example if you add a field in GraphQL that's perfectly fine to not change the version because that break any existing calls, which is not always true with REST.
Assuming you are using some from of Semantic Versioning, adding a field to a REST API should never break existing clients. Did you mean "removing" a field?
Hopefully, it'll be standardized soon.
For writing React Apps GraphQL is wonderful!
For a new product, yes (so far).
The productivity gains from having a client-server interface that is dead simple to reason about more than made up for the initial investment cost. We can refactor API schemas without worrying about breaking existing client code. We also saw performance gains from saving on network round trips.
Most of the arguments against GraphQL that I'm reading here, seem to actually be misconceptions (or the GraphQL ecosystem not being far enough yet).
For some background info this app has a huge feature set, low traffic, B2B, and at most 2 developers working on this app for about 1 year.
For the technical side I chose typescript from the back end and front end (Angular). This mean I could use the same typings from our ORM mongoose.
Very early on I was able to pick tooling that ending up being cutting edge compared to any other tool in any language, namely the tools where graphql-compose, grapqhl-compose-mongoose, and graphql-code-generator. In addition GraphQL lent itself pretty well to dependency injection on the server side.
It took about 6 months to get fully proficient, but it was completely worth it being able to use the same types for the front end and back end. GraphQL even made it possible to bootstrap types from mongoose into the server. A lot of time when dealing with 3rd party services I could copy the docs and turn it into types for the resolvers.
The biggest single benefit of it all was side stepping the middleware of Express.
I didn't realize how awful express middleware are, because you can't really control the middleware chain very well. But the library with graphql-compose made
chaining resolvers extremely precise, which was important.
Overall I love working with GraphQL but I realize that I probably could have done just as well with rest if the backend has decent middleware chaining i.e not express.
For performance there is lots of options that are often built in like client caching, batched queries, whitelist queries, and persisted queries. It worked pretty well in a PWA too.
- types everywhere!
- following the logic of any resolver is usually pretty straightforward.
- true middleware
- learned how to make good rest frameworks in the future.
- hugely steep learning curve
On the server side, it is a mixed bag. Defining a new type system on top of our existing one, and on top of the Avro schemas we have for inter-service communication, it feels... a bit much. Each layer has a purpose so I can't advocate for removal, but it does feel silly at times. Also the documentation on pagination feels incomplete to the point of confusion, and depending on how you search for answers, you might find blog posts that add confusion rather than clarity.
Some additional friction came from the specific version of the Ruby server library we jumped in at. They went a little overboard in the amount of metaprogramming used by the DSL, and I say that as someone who enjoys metaprogramming shenanigans for fun on a Friday night! But it took a long time to understand why adding some custom behavior, in a way that seemed utterly necessary, was not possible (e.g. defining methods inside the DSL's type block did not class_exec them into existence on the resulting class in any reasonable way). They have since released a new version whose main API is class-based, which is much more clear, enables everything I desire, and I am eagerly awaiting next month when we get to upgrade to that and refactor. Thanks to the maintainers for recognizing this was needed, and for doing the heavy lifting to pull it off!
What I enjoy about GraphQL conceptually is that it dodges a certain square-peg-round-hole issue I occasionally saw with REST, but in the same way that leaves me nervous that we'll end up back at a land of inconsistent RPC spaghetti. In an API where an iron-fisted "RESTful Actions Only!" rule is enforced, many domains find just those one or two actions that feel like they are a better fit for their own unique verb, a custom-named action. The rhetorical-and-ontological escape hatch for REST's limited verb set is to have an abstract "resource" that only has, say, a POST option. You can only "create" an "/recalculate_contacts" resource, but never GET, DELETE and so forth. Or you PUT to "/contacts/recalculate", or some other silliness that just feels too clever by half.
Those of us who were scarred by inconsistent, poorly-thought-out RPC spaghetti over the years cling to REST because it brought peace to our nightmares via simple rules that almost fit everything, and as a bonus, really gel with HTTP semantics. Because GraphQL's Mutations really don't specify much of anything, I immediately assumed it would descend into a sort of API moral decadence, but that has not borne out so far.
If anything, I am heartened to see that newer (though not necessarily younger!) developers, who grew up in a world where REST was all they knew, do not have this temptation to go off the deep end with action names, or to craft RPC-esque systems with ill-considered boundaries of behavior. For the most part they want to name things ala REST, because that's what they are used to, except once in a while. So that is what our company goes with in terms of mutations: Wherever possible, name it for the CRUD action it represents. Only use a more unique/one-off verb if it is truly justified.
The tl;dr; is that graphql gives you a lot of flexibility and typed schemas are nice, but it doesn't come for free and I miss the tooling around http.
- Typed schema
- Custom queries retrieve all the data the client needs in one request
- Really easy to implement, both server-side and client-side
- Server-side, it's trivial to have any field resolved in any way you want (redis, memcache, postgres, some random service)
- Easy and arbitrary mutations. There's no pontificating over what verbs are the proper ones to us.
- If you use react, the community and ecosystem often assumes you're using GraphQL, so it may make sense to use graphql just so you don't swim against the current.
- The payloads can quickly become huge because there is often a ton of duplication in a responses (depending on your query patterns). See this example on the SWAPI demo: https://bit.ly/2uOFZBP. The result is 1MB of JSON, ~97% of which is data that exists somewhere else in the response already.
- Refactoring types is often impossible to do in a backwards-compatible way, even if the shape of the data is the same.
- You don't know what data you'll need in advance, so you're basically doing all of your joins by going back and forth between the api resolver and your data sources (this can be alleviated with persistent queries, but those come with their own set of issues). A typical query to hydrate a response for a user's feed in our app requests ~1,100 objects. After caching and consolidating queries into multi-gets, it translates to about 50 distinct DB queries.
- Tooling: Working at the HTTP level simply has better tooling and tons of infrastructure around caching and serving content (varnish, nginx, etc...)
We found that graphql payloads were so large that older mobile phones were spending significant time parsing them. We created graphql-crunch to de-duplicate responses before sending them over the wire. This led to nice perf improvements on mobile platforms. It also gave us referential equality when persisting the results to cache, allowing us to reduce a lot of work client side.
If I were to do it again, I'd at least prototype a REST-api with resources designed specifically for http cache-ability (that is, break out session-specific resources/attributes vs shared resources) and see if HTTP/2 multiplexing + nginx caching + etags results in a good client experience. But I also mostly work on the backend while my co-founder mostly works on the frontend, so we have different desires and constraints. Ideally, as few requests as possible would make it to code that I wrote. With GraphQL that's nearly impossible.