Hacker News new | ask | show | jobs
by hardwaresofton 1950 days ago
Another week, another submission that hopefully edges more people towards realizing that they've just created an under-standardized, under-powered SQL-with-braackets that forces you to bring your own query optimizer, and eschews the advancements of Swagger/OpenAPI, JSONSchema, HAL, JSON-LD and the flexibility of HATEOAS.

All for a little bit of a velocity gain from declarative syntax and horizontal/vertical filtering.

This code is probably very useful to the people who made it and those in the GraphQL+Swift ecosystem, but we were already here (and just starting to get drastically better) with OpenAPI -- it's not like all the effort is only going in one direction but GraphQL really feels like a less-than-optimal branch.

The final piece of the puzzle for me will be when someone starts to replace the dynamic properties of the more advanced REST-ful techniques (API discovery, link following, etc) in GraphQL. Then the circle will be complete -- we'll have rebuilt XML (in the "linked data" respect anyway, not so much the accidental complexity side) and it's related standards twice in under ~20 years.

8 comments

I don’t really think of GraphQL as a data querying language like SQL.

I think of it as a domain querying language.

SQL is meant to allow you to write queries to your data model which are:

- arbitrary, and

- efficient

I don’t think of GraphQL that way. I think of it as the place where you encode your set of valid domain actions (i.e. not arbitrary). And I don’t think the consumers of the GraphQL API should think about efficiency. They should just specify what data they need and then the backend is responsible for figuring out how to query the data model efficiently.

In other words, I don’t really see any overlap between GraphQL and SQL in terms of the role they play in a stack.

One helpful thing this distinction allows, is type inference. You can trivially write a type generator that gives you the type signature of a GraphQL query in any language. This is precisely because of its limitations. That allows you to automate the validation of your frontend and backend speaking the same language.

You can’t easily infer the return types of arbitrary SQL queries. To me, that highlights the different purposes of the languages.

> And I don’t think the consumers of the GraphQL API should think about efficiency. They should just specify what data they need and then the backend is responsible for figuring out how to query the data model efficiently.

That is the point of the SQL language. It's declarative. You define what you want your data to look like and the query planner handles the actual fetching of the information in the most efficient way it can. Obviously it's not perfect and you still have to have someone who knows what they're doing to define schemas that make sense and indexes where appropriate, but that is a separate job from defining what data is needed.

Nitpick: it isn’t SQL that lets you define what you want your data to look like, but DDL (https://en.wikipedia.org/wiki/Data_definition_language)

SQL (https://en.wikipedia.org/wiki/SQL) as the name implies, is just for querying.

I don’t see huge benefits for GraphQL. It’s a query language without a good query planner, so APIs will be limited in the kind of queries they support. In that sense, it’s similar to restricting callers to a fixed set of stored procedures instead of full SQL.

That’s a viable idea, but it can also easily be implemented in REST or using a JDBC connection.

IMO, the main benefit of GraphQL is that it allows the caller to specify what fields it wants to see returned. That means the implementation doesn’t have to send information the caller doesn’t need, decreasing bandwidth, and also doesn’t have to provide a zillion endpoints (give me only the address of user ‘foo’, give me address and telephone number of user ‘foo!’, give me address and birthday of user ‘foo’, etc.) that have to be maintained at the caller’s whim (“we also need the user’s hair color”). That, however, doesn’t need a full query language. REST could easily be extended to support it.

Yes, you could also allow fairly free form GraphQL queries, but if you do, you need a query planner (and, with it, the statistics and metadata that help the query planner perform), or end up with a rat’s nest of special cases that has to be updated for every new type of query.

The great thing about GraphQL is that it's declarative and you have to write your own actual logic to define how the fetching of information works! It only does half the work SQL does! Everyone should switch to GraphQL ASAP!
When your backend is sql how can you even do this efficiently? Databases require indexes. If you can query anything then there are performance bombs all over the place. It's different if you're querying elastic I guess.
> If you can query anything [...]

The answer is simple: You can't. GraphQL _in general_ doesn't allow arbitrary queries. It allows arbitrary output field selection. But the filters are very explicit. It's more "pre-aggregation of request waterfalls and masking of outputs" than "querying a database".

Doesn't stop people from exposing their SQL databases directly from GraphQL by generating a "free-for-all" schema. And when they do - yep, that's definitely a performance bomb and not a good use of GraphQL.

> The answer is simple: You can't. GraphQL _in general_ doesn't allow arbitrary queries.

It really does. Surely, it somewhat limits the data that you get from it by defining a schema. But the moment you allow any nesting/connections between data in that schema, hello n+1 problem.

And then every discussion of this problem on HN or elsewhere exposes the ugly truth: almost everyone uses GraphQL as a REST endpoint in production by limiting the actual queries you can run and curbing nesting.

The n+1 problem has solutions though. The most well-known solutions may not suit your architecture, but please can we stop pretending they don't exist?

GraphQL has been public since June 2015, and there's been at least one solution to the n+1 problem (Dataloader) since September 2015. If you were using pure REST endpoints (just resources, no nesting/traversal) this is the exact problem you'd be punting over to the client to solve -- all that GraphQL is doing here is moving it back onto the server. The actual amount of work is the same, you just get faster response times.

Most implementations of GraphQL I've seen in different languages provide some variation on the Dataloader pattern. I'll fully concede it can be a hassle to set it up correctly, but it works.

> The n+1 problem has solutions though.

It does. But it also means that the problem exists. It's there, you run into it by default, and you have to take special care to make sure it doesn't happen. And data-loaders are just a first step. Some systems try to actually calculate query complexities and nesting depth.

> this is the exact problem you'd be punting over to the client to solve -- all that GraphQL is doing here is moving it back onto the server. The actual amount of work is the same

Exactly. The complexity doesn't go anywhere.

I... don't know how all this is an argument against what I said.

I never fully solved this problem, so don’t trust me, but I can tell you what I learned...

I think for one thing you can’t really rely on joins for query efficiency, because as you say there are too many combinations so it’s impossible to optimize everything.

Instead you have to try to query each data type separately. So you get a query for users. You do an SQL call and gather up a bunch of requests for offices, and then you do a single request to your office backend.

I think the best case is something like n SQL queries per request, where n is the depth of the tree you are querying (users->office->address is depth 3).

That means you’re doing all your queries after the first one by ID (not by arbitrary columns). So you have to have some way to “pre-join” your tables. You can do this either by optimistically joining your data to everything around it (query the node plus all of its edges) or you need to store your edges in your data model (which I have to assume is what FB does).

In the end your resolvers need to be using some standardized way of grabbing objects by is (or edge), something like https://github.com/graphql/dataloader

Whether it’s possible to do this efficiently I don’t know. At my last job we messed it up, and then we started applying a strategy like I described above, but then I switched jobs.

Would love to hear from others who have dealt with the same challenges.

So SQL is not a database :). It is a data access DSL that is implemented by databases. SQL being untyped I dont think is true - the table schemas are types (albeit basic product/record types). Inferring the type of a result is quite reasonable if you start with the schemas. SQl suffers from a UX problem for sure.
If we consider GraphQL to be a domain querying language, what have we gained over REST? You are free to model endpoints in REST according to your domain (and deal with complexity behind the facade), and I'd argue that REST can offer an even more ergonomic DSL interface if you just write whatever you want (s-expressions, let's say) and pass them to some POST endpoint that reads your DSL and parses it. If the idea of writing the DSL and POSTing it, parsing it, and doing the very specific logic you want sounds wrong to you, it seems like GraphQL should also similarly wrong. If it sounds good to you, then is GraphQL far enough?

> I don’t think of GraphQL that way. I think of it as the place where you encode your set of valid domain actions (i.e. not arbitrary). And I don’t think the consumers of the GraphQL API should think about efficiency. They should just specify what data they need and then the backend is responsible for figuring out how to query the data model efficiently.

This is how SQL works, so there is some overlap there. Optimizing SQL queries is might be a performance-seeking operation, but SQL is declarative, and it is left largely to the query optimizer to make your queries run fast. You can help the query optimizer make the query run fast, but that's all you can do -- and I can guarantee you that doing query optimization has not gone away due to GraphQL, you've just pushed the problem somewhere else, or you're forgetting the bits of your API that you've modeled awkwardly in order to avoid performance degradation/difficult-to-write resolvers.

But I think we're a bit off-track here -- GraphQL and REST is at a different level of abstraction than SQL. My point is that we've taken a step back from what we had already with REST rather than that people should be using SQL on the front-end. I think GraphQL is doomed to attempt to reach expressive parity with SQL but that's another conversation all-together.

> One helpful thing this distinction allows, is type inference. You can trivially write a type generator that gives you the type signature of a GraphQL query in any language. This is precisely because of its limitations. That allows you to automate the validation of your frontend and backend speaking the same language. > > You can’t easily infer the return types of arbitrary SQL queries. To me, that highlights the different purposes of the languages.

Most sufficiently ORMs can also give you this, and in other languages there are libraries that will compile-time-check the arbitrary SQL queries you write and won't compile if they're invalid. What you need to have that kind of thing work is sufficient type-checking power (Typescript offers this) and sufficiently rich metadata (there are some examples in the haskell[0] and rust[1] worlds). It wasn't necessary to throw away REST to get these kinds of benefits. I've been quite happy with TypeORM for example, and it would form a good base for this kind of effort -- I don't know a library that's already doing it, but this actually isn't as hard as you think, especially for the simple case.

I'd argue that there is no difference (without too much evidence, to be fair, as I am not an expert in inner working of GraphQL) in the difficulty or parsing and validating a GraphQL query for the simple case (i.e. the actual subset of SQL that GraphQL represents) than actual SQL.

[0]: https://hackage.haskell.org/package/postgresql-typed-0.6.1.2...

[1]: https://github.com/launchbadge/sqlx

> If we consider GraphQL to be a domain querying language, what have we gained over REST?

Part of the value is in standardization. Yes, you can get most of the benefits of GraphQL by creating your own layer over REST, but then you've just written a badly specified, bug-ridden version of GraphQL. The latter has enough momentum that there now exist tons of tools for working with it, which obviously wouldn't be true for anything you build yourself.

> Most sufficiently ORMs can also give you this, and in other languages there are libraries that will compile-time-check the arbitrary SQL queries you write and won't compile if they're invalid.

You're missing the point here: GraphQL gives you type safety between the server and client. This has nothing to do with ORMs or your database. What this means is that, when building your web (or Android, or iOS, or refrigerator, or whatever) frontend, you can guarantee the type of every part of your query before even executing it. This is a powerful guarantee, and paired with something like GraphiQL[0], it allows for a level of exploratory programming that isn't currently possible with REST.

[0]: https://graphql.org/swapi-graphql

> Part of the value is in standardization. Yes, you can get most of the benefits of GraphQL by creating your own layer over REST, but then you've just written a badly specified, bug-ridden version of GraphQL. The latter has enough momentum that there now exist tons of tools for working with it, which obviously wouldn't be true for anything you build yourself.

Right, and you could have done this standardization somewhere else right -- GraphQL is a NIH version of what could have existed on top of well-considered existing standards.

> You're missing the point here: GraphQL gives you type safety between the server and client. This has nothing to do with ORMs or your database. What this means is that, when building your web (or Android, or iOS, or refrigerator, or whatever) frontend, you can guarantee the type of every part of your query before even executing it. This is a powerful guarantee, and paired with something like GraphiQL[0], it allows for a level of exploratory programming that isn't currently possible with REST.

I was responding to the point the person made in particular, I'm aware of the mismatch in tiers, but their question was specifically about being able to infer types from SQL queries. They were comparing SQL to GraphQL. There is nothing stopping you from using compile-time-checking on the client side, if you use something like TypeScript, which is why the rest of the surrounding example was given.

This also is related to the database, because the original point was that people go GraphQL -> Resolver -> DB, rather than HTTP -> HTTP Endpoint -> DB, and because they have chosen GraphQL, they must now write efficient, general resolvers that are essentially hand-built query optimizers whereas with REST you can build with higher granularity and usually higher, easier-to-achieve efficiency.

You can guarantee every part of your query with REST as well, if what you mean is you can avoid writing an invalid query -- Typescript + generated OpenAPI client libraries do this very well -- it's not unique to GraphQL.

> Right, and you could have done this standardization somewhere else right -- GraphQL is a NIH version of what could have existed on top of well-considered existing standards.

Maybe, but I think building GraphQL on top of REST would have produced something much more convoluted and verbose. As an example, see the OData standard[0], which, at least from the client's perspective, is unnecessarily complex compared to GraphQL. There may be cleaner ways to structure this, but I'm not aware of any such attempts, and I doubt you'd get a result as easy to use and understand as GraphQL.

> This also is related to the database, because the original point was that people go GraphQL -> Resolver -> DB, rather than HTTP -> HTTP Endpoint -> DB, and because they have chosen GraphQL, they must now write efficient, general resolvers that are essentially hand-built query optimizers whereas with REST you can build with higher granularity and usually higher, easier-to-achieve efficiency.

That's fair, but as you pointed out earlier, you're just shifting complexity around in any case. REST is simple because it's so granular, which is great when all of your data needs are met by that granularity. As soon as you have more complex data requirements, you end up either making ad hoc endpoints or evolving your API into a giant monstrosity so that you can handle the more general cases. GraphQL significantly simplifies this implementation on both the client side and the API side. Yes, the tradeoff is that now you need to optimize your data access manually, but as others have said, there are now tools for dealing with this issue—and it's not like you don't gain anything from it.

For a simple but concrete example, if I need to fetch data from ten different entity types in order to render a page in my web app, I can do so in a single GraphQL query and handle caching, etc in one pass, as opposed to needing ten different REST calls and having to consolidate them by hand. On top of the maintainability benefits of this approach, the productivity gains are huge.

[0]: https://www.odata.org/

> Maybe, but I think building GraphQL on top of REST would have produced something much more convoluted and verbose. As an example, see the OData standard[0], which, at least from the client's perspective, is unnecessarily complex compared to GraphQL. There may be cleaner ways to structure this, but I'm not aware of any such attempts, and I doubt you'd get a result as easy to use and understand as GraphQL.

I'd never heard of OData, thank you very much for the link! I've been meaning to give it a shot (try to put together a GraphQL using the other REST tools/standards), if I do I will definitely post it to HN. I agree that it would likely look more complex in the naive case but that could be ironed out -- the foundations feel identical. GraphQL's productivity and ergonomics are definitely something to strive for on the REST side.

> That's fair, but as you pointed out earlier, you're just shifting complexity around in any case. REST is simple because it's so granular, which is great when all of your data needs are met by that granularity. As soon as you have more complex data requirements, you end up either making ad hoc endpoints or evolving your API into a giant monstrosity so that you can handle the more general cases. GraphQL significantly simplifies this implementation on both the client side and the API side. Yes, the tradeoff is that now you need to optimize your data access manually, but as others have said, there are now tools for dealing with this issue—and it's not like you don't gain anything from it.

True -- growth of REST API endpoints is somewhat unbounded and it gets harder and harder to differentiate/name endpoints and separate functionality in a satisfying way. There was another comment[0] that showed the gains that a small team got from GraphQL, I can't argue with that. It does work extremely well for some teams.

> For a simple but concrete example, if I need to fetch data from ten different entity types in order to render a page in my web app, I can do so in a single GraphQL query and handle caching, etc in one pass, as opposed to needing ten different REST calls and having to consolidate them by hand. On top of the maintainability benefits of this approach, the productivity gains are huge.

This has been an oft-cited benefit of GraphQL, and it looks like a legitimate clear benefit -- not having to think about that is indeed very nice.

[EDIT] - Yeah after poking around OData for a bit, I don't think I'll be touching that but it's nice to know it exists.

[0]: https://news.ycombinator.com/item?id=26226642

I've found GraphQL to greatly improve the developer experience when building web apps. As a front-end developer you can easily get a full view of the data and relationships. Tools like GraphiQL make exploring APIs a pleasure. And regardless of what you need to present to the user on screen, you can quickly build a request that perfectly matches the data you need. There is also the nice addition of strict typing and there are libraries that automatically generate TypeScript types for you from the schema and/or your operations.
I do agree that GraphQL has improved developer experience, or at the very least improved the perceived experience (which is essentially the same as actually improving the experience). I cannot deny it's popularity, and that comes from hype along with usefulness (perceived or actual).

> Tools like GraphiQL make exploring APIs a pleasure.

This is an innovation of the developers of GraphiQL, not of GraphQL itself. It's possible to build IDE support similar ot that of GraphiQL, with the rich metadata and schema data that is available by design in OpenAPI + JSONSchema + JSON-LD + HATEOAS land. The problem is that no one did/was excited enough to, the slog from Swagger2 to OpenAPI3 might have sapped the enthusiasm of the community just enough (or the emergence of GraphQL), but it's not that the tools isn't possible with other approaches.

> There is also the nice addition of strict typing and there are libraries that automatically generate TypeScript types for you from the schema and/or your operations.

This was already present with OpenAPI and related tools, so I personally don't put this as something that GraphQL brought about.

I don't want to piece together 4 different technologies to get the same thing.

And it's not even the same thing. You keep mentioning HATEOAS but that doesn't accomplish the same things as graphql at all. With HATEOAS I need to send out multiple requests to collect the data I need. With graphql I can send out one request to retrieve exactly the data I need. I can also combine multiple requests into a single request. GraphQL also only retrieves the fields you need by default. This is something you'd need to build manually with REST.

And yes, reducing the number of server requests does matter when you're trying to optimize for the largest market possible. Including people on slow mobile network connections where every request adds significant overhead.

> I don't want to piece together 4 different technologies to get the same thing.

> And it's not even the same thing. You keep mentioning HATEOAS but that doesn't accomplish the same things as graphql at all. With HATEOAS I need to send out multiple requests to collect the data I need. With graphql I can send out one request to retrieve exactly the data I need. I can also combine multiple requests into a single request. GraphQL also only retrieves the fields you need by default. This is something you'd need to build manually with REST.

All of this is reasonable, but my point was that we've abandoned a more flexible method, that had composable functionality and agreed-upon standards for GraphQL. You gained some pipelining, and some vertical filtering, but threw out a lot with the bathwater.

GraphQL does give you a way to do these things, and this is why it is popular (or at least a good reason why), but it is missing the wider possibilities of the other ecosystem. As people start to try and abstract over it they will be abstracting over a less robust, considered, standardized base.

> And yes, reducing the number of server requests does matter when you're trying to optimize for the largest market possible. Including people on slow mobile network connections where every request adds significant overhead.

Reducing the number of server requests does indeed matter -- I did not mean to propose it doesn't, reducing request was somewhat solved before now with the backend-for-frontend approach. There are lots of approaches to help people on slow mobile network connections, but the ultimate one is to server-render and trim as much as possible -- I'm not sure GraphQL is much better at this than REST with efficient endpoint choice.

> You gained some pipelining, and some vertical filtering, but threw out a lot with the bathwater.

What are we missing that actually matters in practice? And yes, if you minimize the problems that graphql was built to solve it seems like a worse solution.

> As people start to try and abstract over it they will be abstracting over a less robust, considered, standardized base.

Facebook, a company who's apps serve billions of people came up with graphql to solve real problems. Saying it isn't well considered is asinine. As for standardization, last time I checked GraphQL has a spec and the GraphQL Foundation is a member of the Linux Foundation.

Again, what problems are you talking about that graphql doesn't solve? I mean actual, practical problems.

> reducing request was somewhat solved before now with the backend-for-frontend approach.

So now I need to write a backend for each use-case. That sounds better. Or I need to make my endpoints extra configurable which starts to approach graphql territory.

> the ultimate one is to server-render and trim as much as possible

Again, we're now entering the "why graphql was invented" territory. Sure I could add these to my REST endpoints.. OR I could write one graphql endpoint and get all of these.

> What are we missing that actually matters in practice? And yes, if you minimize the problems that graphql was built to solve it seems like a worse solution. > Again, what problems are you talking about that graphql doesn't solve? I mean actual, practical problems.

If you're using GraphQL and feel that it solves all your problems and isn't painting you into a corner, you are free to continue to use it and reap the efficiency rewards. I'm not here to convert you to REST or any other RPC methodology. One of the things that was lost with adopting GraphQL is partial responses -- being able to send part of the answer back before the entire request is complete. I'm not sure exactly what streaming looks like in GraphQL but do they have anything as simple and useful as SSE?

> Facebook, a company who's apps serve billions of people came up with graphql to solve real problems. Saying it isn't well considered is asinine. As for standardization, last time I checked GraphQL has a spec and the GraphQL Foundation is a member of the Linux Foundation.

I did not mean to imply that GraphQL was not considered -- just that it is less considered than the alternatives that existed, and certainly was less battle tested. Just because something has a spec does not mean the spec is good and well reviewed (though again, I'm sure GraphQL's has been looked at by smart people for large periods of time). I don't know what to make of the GraphQL Foundation being a member of the Linux Foundation,

> So now I need to write a backend for each use-case. That sounds better. Or I need to make my endpoints extra configurable which starts to approach graphql territory.

It's not that writing robust endpoints is approaching GraphQL territory, it's that GraphQL territory is bootstrapping robust endpoints for you, but it also locks you in to it's way of viewing the world. I prefer to use interoperable smaller tools rather than doing this, and maybe I'm losing out because of that, but I'd take the complexity of one or two or five robust endpoints over adoption of the entire GraphQL spec & ecosystem for what I'd consider minimal gain. To each their own.

> Again, we're now entering the "why graphql was invented" territory. Sure I could add these to my REST endpoints.. OR I could write one graphql endpoint and get all of these.

No, that was me pointing out that if you're trying to help low bandwidth clients, that's how you definitively do it, GraphQL is not the right solution in that case -- you try your best not to make any calls on the frontend at all, so it seems the point is moot there.

Also it can potentially reduce the client side logic needed to stitch said 4 requests together.

This can be a big complexity/bug preventer for multi client Apps (web, mobile, desktop).

To what extent are you just pushing complexity to the back-end developers?

> Tools like GraphiQL make exploring APIs a pleasure.

No argument there. Is there something similar for traditional REST? For some reason, I thought the point of HATEOS was to make that kind of exploration possible.

GraphQL cannot ever be explored in the same way HATEOAS can because it is missing the linking part (and other related technology like JSON-LD). That said, GraphQL does offer introspection[0] which is not a bad start but lacks the breadth an depth of the hyperlink based approaches.

[0]: https://graphql.org/learn/introspection/

I 100% agree with your statement. I also realize you are not championing HATEOAS here necessarily.

That said, I am curious if you have found this "linking" aspect of HATEOAS useful for actual implementations? I have been doing integration work with a system that strictly adheres to "REST level 3" and "HATEOAS" principles for the past few years, and I myself have found the "explore-ability" of the API super handy. That said, the self-documenting nature only goes so far, and in the end I'm not sure the internal linking stuff is preferable to robust documentation.

I'm not trying to be down on this necessarily, I actually generally push back on the adoption of GraphQL as the answer to every problem.

> That said, I am curious if you have found this "linking" aspect of HATEOAS useful for actual implementations? I have been doing integration work with a system that strictly adheres to "REST level 3" and "HATEOAS" principles for the past few years, and I myself have found the "exploitability" of the API super handy. That said, the self-documenting nature only goes so far, and in the end I'm not sure the internal linking stuff is preferable to robust documentation.

You're right, this is a reasonable question and the answer is uncomfortable. I personally find it useful when paired with OpenAPI -- generally by using annotations on controllers and models, but it is indeed rare to have usecases that fit the linking functionality well enough to be significantly better than what you would get from just good documentation.

The "killer app" of this space for me personally is a Django Admin[0][1] (or React-Admin[2]) clone that is 100% client-side automated. I don't have a demo yet, but once I do it'll be up on HN.

> I'm not trying to be down on this necessarily, I actually generally push back on the adoption of GraphQL as the answer to every problem.

Please, feel free to push back, that's what discussion is for, and ideas that can't stand up to push back probably shouldn't be adopted.

[0]: https://docs.djangoproject.com/en/3.1/ref/contrib/admin/#

[1]: https://djangobook.com/mdj2-django-admin/

[2]: https://github.com/marmelab/react-admin

I would like a backend developer perspective on the same.

I haven't done any GraphQL stuff myself, but the "I can get whatever data I need" aspect feels like a huge potential headache for backend devs. Won't be a problem at prototype scale, but once you have a significant client count, how do you deal with unpredictable data access patterns that can't be optimized ahead of time?

Generally speaking, it's not more difficult than creating a REST API controller. Rather than mapping your service code to a controller, you map it to a (GraphQL) resolver. It feels extremely similar.

There are different things to look out for; like handling n+1 (https://shopify.engineering/solving-the-n-1-problem-for-grap...) queries, but nothing that I can say is too difficult.

It is more difficult. With REST you know ahead of time which data you need, and you can optimise it as much as you can. With GraphQL at any given point in time you have no idea what the request is. Dataloaders solve it to some extent, but not much.
Of course, you are building something that is much more powerful. If you intend every possible GraphQL operation to perform perfectly, it'll take more development effort. Though if you optimize for only the most likely cases, the effort is not much greater.
> Though if you optimize for only the most likely cases, the effort is not much greater.

That's why most discussions involving complexities of GraphQL inevitably devolve into: "in production we only allow a subset of queries". Because actually implementing GraphQL as it's specified and marketed is quite an undertaking.

Inevitably you do get a map of your data flow. GraphQL can be a huge help here, because you must have resolvers for the queries. It forces you to think ahead of time about how you're going to resolve getting the data to fulfill the API "contract" if you will. You can optimize that. GraphQL is great for describing your APIs in a type safe(ish) way where you get great decoupling by design.

I also will say that since a query can use multiple resolvers, that's where I have found it really shines. The model does implicitly work best when you have async systems over synchronous ones for more complex queries (multithreaded/multiprocess)

that's my anecdote, as someone who is currently and actively building and maintaining GraphQL backend services.

Its true you can get the same with any API design pattern, really, I will say, however GraphQL has specifications for all this, and I think that's what makes it more powerful

Another nice thing, is no versioning. I can just use the `@deprecated` built in directive, and when the usage of a deprecated part of an API gets consistently (for a specified period of time) down to 0, I can just remove it entirely.

OpenAPI and the like don't have a descriptive way to notify users of an API of this, you, for better or worse, have to version your API, which often ends up in situations where you always have an old version sticking around for a very long time.

Like all technologies though, it can (and does) have its issues. It adds a certain amount of complexity to your applications (more so for the client, IMO, even though exploring APIs is a huge upside with tools like GraphiQL)

I don't have especially strong feelings either way, but the majority of this is possible with OpenAPI as well.
The main advantage I’ve found with GraphQL is the normalized cache in client libraries like Apollo or Relay. It allows components to just ask for the data and not worry about how it’s fetched/stored/updated. The secondary benefit is that _some_ (not all) of the data plumbing can be moved from the frontend code to the backend, which makes for faster apps. Beyond that, GraphQL is more of a hinderance than a help... its type system in particular is brain dead and easily the worst part of the entire experience (seriously, why _is_ there a difference between inputs and types? Why can’t you define input unions, or maps, or recursive structures?)

Is there any equivalent for the latter with swagger? Yes, you can do everything in there with custom redux middlewares or sagas/observables, but it’s more glue code you have to write and maintain.

> The main advantage I’ve found with GraphQL is the normalized cache in client libraries like Apollo or Relay. It allows components to just ask for the data and not worry about how it’s fetched/stored/updated. The secondary benefit is that _some_ (not all) of the data plumbing can be moved from the frontend code to the backend, which makes for faster apps.

Is this something you couldn't achieve with the idiomatic rules on HTTP request types, and serialization of HTTP requests parameters? It really depends on where you're trying to do the caching and what kind of caching you're trying to do.

A lot of work has gone into HTTP headers, method semantics, and other details in order to facilitate caching. The "backend for frontend" pattern has been very popular as of late in order to try and shift the plumbing load, exposing endpoints that are catered to certain clients to boost efficiency -- would that be good enough for a corollary in OpenAPI land?

The backend for frontend pattern is exactly why I got sold on GraphQL. We had a pile of relatively low value server code that could be replaced a GraphQL resolver and the client got all of the advantages of that pattern. The move to GraphQL genuinely freed up a lot of our time to work on more important problems. This was on a smallish team so that time mattered.
It looks like you've picked the right tool for the job in that case, and I can't argue with that -- but the submission is for tooling that people have created in Swift for a problem that was already relatively solved by OpenAPI bindings for Swift.

Would you mind going into what that low value server code was doing? Was it simply aggregating results of multiple requests? It's quite possible it wasn't that hard to solve with slightly more intelligent (but not a completely separate paradigm) code to start with. However all-in-all I'm not out to argue that GraphQL doesn't improve velocity. I'm saying that the tooling that it has built in (pipelining, horizontal/vertical filtering, etc) could have been available from REST, and gained all the other benefits without a dramatic paradigm shift but no one seems to have bothered, and instead we're finding ourselves rebuilding bits that were already available/worked out in OpenAPI land for a few features that weren't too far away/hard to implement there.

FWIW this was ~2015ish, I'm not sure what the state of OpenAPI was at the time.

> Was it simply aggregating results of multiple requests?

Pretty much. The company I was at was an early proponent of microservices so we had to have a service that operated as our entry point to all of it. Thats easy enough, but layering on versioning of the APIs for native apps on different platforms added complexity that definitely didn't seem worth dealing with in our at the time REST paradigm. We did have frameworks that generated clients for different platforms against that API so I'm not even talking about the boilerplate of sending and parsing JSON.

Cool thanks for sharing this -- it's come up multiple times in this thread and it seems I didn't weigh this point enough.

Maybe this is enough to really make GraphQL or a similar system worth it -- GraphQL and Falcor both existing as similar but separate technologies probably says something about just how problematic non-GrapQL-like patterns are at scale, and if your had a good experience at smaller scale as well then it's worth reconsidering.

There is no different paradigm. Both GraphQL and "REST" as it's usually implemented are RPC protocols. GraphQL just doesn't pretend it is not.
If you do a survey of front end data fetching frameworks, they basically land in two categories:

- API-agnostic: react-query, rtk-query, swr

- Declarative queries (e.g. GraphQL): apollo-client, relay, urql

A major difference to notice is that the API-agnostic libraries do not handle normalizing the data before caching it. Instead, they cache the data based on some key related to the request. So you can potentially fetch and cache two different versions of the (semantically) same information, leading to UI incoherence. The various API-agnostic libraries have ways of solving this by explicitly creating relationships between different requests so that you can manually say, "when I fetch with this key, also trigger a re-fetch of any components subscribed to these other cache keys." This is coarse, but for many cases "good enough" in contexts where you don't have full control over the API you are fetching data from.

In order to leverage a normalized cache, the layer between the cache and network needs to intimately understand what data is returned by which request so that it can support things like stale-while-revalidating, re-rendering on refetch of the same data from another part of the page, etc. Describing all of the data that could possibly be returned by an endpoint is tedious and error prone, as APIs can be dynamic in what they return and backend changes could easily lead to breaking any assumptions the frontend might make about an opaque endpoint.

Now if you have more control over how a backend behaves and buy-in on solving this problem full stack, you can start to build some of these assumptions into both layers. For instance, you could require all endpoints to be fully documented via swagger, and the front end could use the swagger JSON doc to map endpoints to data potentially returned by a response. You would also need a pattern to key each request with what entity (type + ID) it is responding data with before it completes; HATEOAS provides a standard of relating URIs to entities and would solve this problem.

So you can see that this normalized caching problem can be solved without GraphQL, as long as the backend and frontend can agree on a set of standards and requirements such as REST + HATEOAS + Swagger. Personally, if someone is passionate about solving this problem, I would recommend GraphQL, but it is up to each team to decide on what solutions meet their requirements.

If you just want the syntax and caching, you might try React Query. Haven't used it myself but it looks like it's Apollo-inspired.
Does OpenAPI have a way for the client to whitelist fields? I see that as a major selling point for GraphQL.

Without it, you end up having to create tailored endpoints for each use-case vs. for example a single “user” endpoint/vertex that supports the complete buffet of fields for all use-cases in one convenient place.

I can't say that it should have that feature -- I'd say that is outside the purview of OpenAPI itself, but it could definitely be built in in a repeatable and scalable way pretty simply. I can tell you that tools like PostgREST have had this for a long time[0].

[0]: https://postgrest.org/en/v7.0.0/api.html#vertical-filtering-...

The one place where I've found GraphQL to be potentially useful is back office. This is usually the place where you need a lot of very different data in one place, and in ways that are completely different from customer-facing apps or sites. And back office usually doesn't get as much love.

So usually you have backoffice devs scrambling to retrieve data in weird ways from existing APIs. And things like "does this inventory item come from a provider with an active contract, and who else is on that contract" become ... weird and unwieldy. This is where GraphQL with it's ad-hoc queries definitely helps. And since the volume of queries in back office is low, you don't worry too much about the performance of some of the queries.

I wouldn't use it for anything else though.

Anecdata:

Just last month it took me about half an hour to implement an "omni search" functionality in GraphQL that takes string as input, coalesces results from three different backend APIs and return back a nice view to fuzzy-ish search for three different entities in our backend. Frontend couldn't be happier.

Does OpenAPI support multiple kinds of data in a single HTTP request? I couldn’t find anything about that in the docs.
AFAIK there is not a standardized way to do this but there's no reason it can't -- you are free to define your own Content-Type or use framed binary, or whatever else. If you already have schemas on either side (or code to do dynamic discovery), and a way to pack/unpack you can send different kinds of data across.

This is less ideal than GraphQL in this case, since GraphQL has done this hard work and standardized it/created the conforming reference clients/servers already.

> replace the dynamic properties of the more advanced REST-ful techniques (API discovery, link following, etc) in GraphQL

Isn't this exactly what GraphQL does? I may be missing something

100% agree. People also need to remember that GraphQL was originally written for server side javascript, and now it’s being ported to other languages(for what reason???).

You can create your own global json schema and build a ratchet version of GraphQL using proven orms in about 20 minutes using an unpersisted “filtering” field.

If you want automatic access to your data, bite the bullet and do something cool like implement Apache Dremio.

GraphQL was released as an specification with a node example implementation. It wasn't "written for server side javascript".
Ok, so it was software released with it’s first implementation written in node.

And now have “Apollo”, which is a server that runs inside a v8 engine(node).

It seems to me that the javascript crowd are the ones pushing this forward and seems to me that it was written with javascript as a first class citizen.

Hard pass