I wouldn’t expect the performance issues to be much more problematic than they would be for REST endpoints that offer similar functionality. If you’re offering a public API, then either way you’re going to need to solve for clients who are requesting too many expensive resources. If you control the client and the server, then you probably don’t need to worry about it beyond the testing of your client code you would need to do anyway.
As far as query optimization goes, that’s largely out of scope of GraphQL itself, although many server implementations offer interesting ways to fulfill GraphQL queries. Dataloader is neat, and beyond that, I believe you can do any inspection of the query request you want, so you could for example see the nested path “Publisher -> Book -> Author -> name” and decide to join all three of those tables together. I’m not aware of any tools that provide this optimization automatically, but it’s not difficult to imagine it existing for some ORMs like those in Django or Rails.
query {
user {
name
email {
primary
secondary
}
posts {
body
karma
}
}
}
It would create an entire database schema with users, emails, and posts, and the correct indexes and fk relations to support the graphql query. It would also generate mutations for updating the entities in a relational, cascading manner, so deleting a user would also delete their email and posts.My impression is GraphQL starts to shine when you have multiple backend systems, probably separated based on your org chart, and the frontend team needs to stitch them together for cohesive UX. The benchmark isn't absolute performance here, it's whether it performs better than the poor mobile app making a dozen separate API calls to different backends to stitch together a view.
I honestly think the backend benefits are relatively marginal, but on the client being able to 1) mock your entire backend before it even exists, 2) have built in automatic caching on every query and entity for free, 3) use a fully declarative, easily testable and mockable React hook API built with out-of-the-box exposed support for loading and error states, is so valuable. Components written against Apollo feel so clean and simple. That's not to say anything about the benefits you get from being able to introspect the entire backend API or test queries against it with Apollo's extensions or how you can easily retain the entire entity cache across reloads with simple plugins to Apollo.
Can you do all that with REST? Sure. But writing queries against REST APIs is a pain in the butt compared to what you get for free by using Apollo.
> But even then there's still some value in letting the client specify the shape of the the data it needs and having client SDKs
It may not exactly "shine" in those cases, but it reduces round trips and makes it easy for fronted engineers to make views that revolve around use cases instead of the resources in the database.
Is that really a big deal with HTTP2 and pipelining?
I can also imagine situations where it results in a better UX to make multiple small calls, rather than one big one, as you'll have something to render faster.
> Graphql solves for this by being able to fetch the book detail and the joined author name with one round trip
I don't see why we need GraphQL to solve this though - a REST backend could have an endpoint that returns the exact same data.
I can see how GraphQL might be somewhat nice for front end developers when the data to be displayed hasn't been nailed down yet - maybe we decide to show the book's ISBN number, and we can do that without changing the backend. Maybe this justifies the extra complexity for some teams, but I'd personally much prefer the simplicity of a REST API, to which you can always add OData on top if you really want.
It's the power of the frontend teams to create a rest endpoint out of a schema.
When you combine it with typescript/JVM/Swift, you get AUTO typing for the graphql queries, you know exactly the data model you get back based on your query. It's quite lovely.
The other aspect is that on the apollo/graphql server you can utilize dataloader and streamline a nested request into as few calls to each service as possible.
And the last benefit over a rest service. If you had to make multiple calls, you're doing round trips from the CLIENT to the backend services. The graphql server is _already_ in your backend service network, so all the data combining is on the orders of <10ms versus <100ms (or much worse for mobile).
GraphQL has a major advantage over rest in that you can't just change the schema without the clients breaking, so you know that your API isn't going to magically just screw you. (Most places use versioning for this, but not always). You can get some of this with RPCs but it's not as robust as the graphql schema.
Actually stitching together multiple services or DBs with it manually seems like it'd be a hellish experience that'd end in a massive data breech or repeated accidental dataloss + restore-from-backup. Or else valid GraphQL going to such a system would be so restricted that the benefit over just using REST (or whatever) is zero.
These are largely matter of architecture design and graphql doesn't really fix those problems (my sense is it will make those problems harder actually).
Without it or a similar system frontend developers have to ask backend developers to create or modify an API endpoint every time the website is redesigned.
Also, it allows to combine data fetching for components and subcomponents automatically without having to do that manually in backend code, and automatically supports fine-grained caching of items.
A major issue with pushing it to the frontend is that malicious clients can issue unexpected requests, putting strain on the database.
If the graphql query implementation doesn't allow that level of querying on the database, then it's not offering much more before you need to speak to the backend devs than a filterable rest endpoint.
This all came up years ago with OData.
A REST endpoint on the other hand is fairly simple and understood; there's (mostly) a static set SQL queries behind it and as long as those are not returning any unwanted data you are pretty much guaranteed to not expose something you didn't want to.
REST endpoints are usually way more blackbox.
You can't claim that REST is better cuz you can look at the server... when you could do the same thing to the graphql server.
Graphql will -never- return you unwanted data. Because you wrote in the query exactly what you want.
If you want to examine an endpoint and JUST what it returns, you can do so really easily with graphiql.
https://developer.github.com/v4/explorer/
Just enter the api and you get an auto complete list of all the data fields you have access to. Or just use the schema explorer and click through. 100x easier than going through a sql query and analyzing a table.
But couldn't you intentionally or unintentionally write a query such that it returns too much data and borks the system? Un-intentionally is the worrisome aspect.
While it might be orthogonal to the design decision, it might add to the amount of unanticipated work that will be required just because of the enormous flexibility.
A typical product would require integrations with several existing APIs, and potentially some new ones. These would be aggregated (and normalised) into a single schema built on top of GraphQL. Then the team would build different client UIs and iterate on them.
By having a single queryable schema, it's very easy to build and rebuild interfaces as needed. Tools like Apollo and React are particularly well suited for this, as you can directly inject data into components. The team can also reason on the whole domain, rather than a collection of data sources (easier for trying out new things).
Of course, it would lead to performance issues, but why would you optimise something without validating it first with the user? Queries might be inefficient, but with just a bit of caching you can ensure acceptable user experience.
You'd want code gen to easily wrap REST services.
You could get some of the pipeline query/subquery stuff back (and lose caching) by setting up a proxy running this service or fallback to client side aggregation to span services not backed by the graph system (and maybe keep caching).
Maybe we're back to SOAP and WSDLs, though.
I've seen GraphQL schemas being implemented on the client, it's certainly doable, but the performance is terrible compared to doing it on a server close to the source of truth.
Not exactly client side, but the main consumer is the client and s2s or service to service isn't intended target.
I'm not sure if it is the implementation - and it could very well be - but there has been more overhead and complexities than with traditionally accessed REST APIs. I can't see much value-add.
This becomes a lot more apparent when you start to include TS in the mix.
Perhaps it just wasn't a good use case.
Most people want #1, Graphene is a bad choice because you still have to write a lot of boilerplate code. It has the added benefit that the current process is responsible for parsing the GraphQL query and directly calling the database, vs. using something like Prisma/Hasura which (may) require a separate process which in turn calls your database (so 2 network hops).
GraphQL was never intended to be an ORM replacement, but many have steered it towards that direction. It's not a bad thing, but it's still the same level of abstraction and confusion that people have wrestled with when using traditional ORM's except now you're introducing a different query API vs. native code/fluent interfaces/SQL.
While Hasura + GraphQL can be used as an ORM (especially for serverless functions!), Hasura is designed to be used directly by clients as well.
Hasura has a metadata configuration system that works on top of an existing database that allows configuring mapping, relationships and most importantly permissions that make the GraphQL feasible to be used by even frontend apps. [1]
Further, Hasura has remote joins that can "join" across Postgres, GraphQL, REST sources and secure them. [2]
[1] https://hasura.io/blog/hasura-authorization-system-through-e...
[2]: https://hasura.io/blog/remote-joins-a-graphql-api-to-join-da...
It's attractive primarily to frontend developers. Instead of juggling various APIs (oftne poorly designed or underdesigned due to conflicting requirements and time constraints) you have a single entry into the system with almost any view of the data you want.
Almost no one ever talks about what a nightmare it becomes on the server-side, and how inane the implementations are. And how you have to re-do so many things from scratch, inefficiently, because you really have no control of the queries coming into the system.
My takeaway from GraphQL so far has been:
- good for frontend
- usable only for internal projects where you have full control of who has access to your system, and can't bring it down because you forgot an authorisation on a field somewhere or a protection against unlimited nested queries.
XgeneCloud makes it really simple to add business logic for generated APIs (REST and GraphQL both) over any SQL databases.
We just launched this week [2]
[1] : https://github.com/xgenecloud/xgenecloud
[2] : https://news.ycombinator.com/item?id=23466782
Website : https://xgenecloud.com
(disclaimer: founder here)
https://hasura.io/docs/1.0/graphql/manual/remote-schemas/ind...
https://hasura.io/blog/remote-joins-a-graphql-api-to-join-da...
If you need to convert your API's into a GraphQL first, you can wrap the endpoints with resolvers yourself, or use automated tooling:
Also, how do you handle transactional logic?
I'm working on a REST code generator (generates a Go backend and a typescript/react frontend) that reads your postgres/MySQL schema and some additional metadata you provide (should auth be enabled? Which table is the users table and which columns are username and password stored as bcrypt). I'm still working on authorization part but basically optional per-endpoint logic DSL for simple stuff and optional Go hooks for more complex stuff.
I'm not even sure about this part.
I worked on a project recently where the mobile front end team hated working with GraphQL. They were far more used to working with REST/HTTP APIs, and in this particular project they only communicated with a single backend.
The team saw it as extra layers of complexity for no benefit.
The GraphQL backend was responsible for pulling together data from several upstream systems and providing it to clients. But the architect never was able to convince me of a single benefit here compared to REST.
As someone who is building a public facing GraphQL API, I would disagree with this. Directives make it easy to add policies to types and fields in the schema itself, making it amenable to easy review.
A restful API also has the problem that if you want fine grained auth, you'll need to remember to add the policy to each controller or endpoint, so not that different.
The typed nature of GraphQL offers a way of extending and enriching behavior of your API in a very neat, cross cutting way.
For example we recently built a filtering system that introspected over collection types at startup to generate filter input types. We then built middleware that converted filter inputs into query plans for evaluation.
I previously worked at another company that offers a public REST API for public transport. Public transport info is quite a rich interconnected data set. Despite efforts to ensure that filtering was fairly generic, there was a lot of adhoc code that needed to be written to handle filtering. The code grew exponentially more complex as more filters were added. Maybe this system could have been architected in a better way, but the nature of REST doesn't make that exactly easy to do.
Bottom line is that I feel for public APIs, that there is a lot of demand for flexibility, and eventually a public facing RESTful API will grow to match or even exceed that of a GraphQL API in complexity.
This is dependent on the framework, just as it is with GraphQL - for example, with ASP.NET Core you can apply an auth policy as a default, or by convention.
> Despite efforts to ensure that filtering was fairly generic, there was a lot of adhoc code that needed to be written to handle filtering.
I've never seen this problem with REST backends myself, but I work with a typed language, C#. Again though, this is more of a framework thing than a REST/GraphQL paradigm thing.
I actually think that unless your company is massive or has a lot of expertise in GraphQL already, using it for private APIs may not be the best idea, as it could be a sign of certain internal dysfunctions or communication problems within or between engineering teams.
----
An example, however of the kind of filtering I was referring to, and why I still think it would be non trivial to do, even in something like ASP.NET, is the following: https://www.gatsbyjs.org/docs/graphql-reference/#filter. This of course isn't something you get out the box in GraphQL either, but the structure of the system made this (relatively) easy to do.
Of course you could add something like OData to your REST API which would definitely be a valid alternative, but that also would have its own warts, and is subject to similar criticisms as GQL.
Ah, then I misunderstood; I was thinking along the lines of dotnet's authorisation filters.
Filtering might require some reflection, expressions or funcs, which aren't necessarily "everyday" things for some devs, but they shouldn't pose any real trouble for seasoned dotnet devs. If you really want a standard that works OOTB for Entity Framework (and I assume EF Core), you have the option of OData too.
You might get away with it in a GraphQL implementation because you can possibly slap it in top a centralized endpoint, but I really question its efficiency in this case.
Query chaining/batching and specifying a sub-selection of response data seem like solid features.
The graph schema seems to make good on some of the HATEOS promises.
I like the idea of GraphQL but the downsides have me worried.
What do you consider the downsides?
GraphQL formalizes the contract between front and back end in a very readable and maintainable way, so they can evolve in parallel and reconcile changes in a predictable, structured place (the GraphQL schema and resolvers). And it allows the frontend, with Relay, to deal with data dependencies in a very elegant and performant way.
It clearly looks questionable adaption for a single organization.
You need to have data loader (batching) on the backend to avoid n+1 queries and some other similar stuff with cache to improve the performance.
You also have cache and batching on the frontend usually. Apollo client (most popular graphql client in js) uses a normalized caching strategy (overkill and a pain).
For rate/abuse limiting, graphql requires a completely different approach. It's either point based on the numbers of nodes or edges you request so you can calculate the burden of the query before you execute it or deep introspection to avoid crashing your database. Query white listing is another option.
There are few other pain points you need to implement when you scale up. So yeah defo not needed if it's only a small project.
This sounds like disaster waiting to happen.
In REST (and seemingly in gRPC), you define these siloed endpoints to return different types of data. If we're imagining a Twitter REST API, you might imagine Tweet and User endpoints. In the beginning it's simple - the Tweet endpoint returns the message, the user ID, and some metadata. You can query the User
Then Twitter continues to develop. Tweets get media attachments. They get retweets. They get accessibility captions. The Tweet endpoint expands. The amount of information required to display a User correctly expands. Do you inline it in the Tweet? Do you always require a separate request to the User, which is also growing?
As the service grows, you have this tension between reusability and concision. Most clients need only some of the data, but they all need different data. If my understanding of gRPC is correct, it would have this similar kind of tension: business objects that gain responsibilities will likely gain overhead with every new object that is added, since the clients have no way of signaling which ones they need or don't need.
In GraphQL, you define your object model separately from how it's queried. So you can define all of these things as separate business objects: a Tweet has a User, Users have Tweets, Tweets can link to Media which has accessibility caption fields, etc. Starting from any entry point to the graph, you can query the full transitive closure of the graph that you can reach from that entry point, but you don't pay for the full thing.
This relaxes the tension between reusability and concision. Each querier can request just the data that it needs, and assuming the backend supports caching and batching when appropriate, it can have some assurances that the backend implementation is only paying for what you use.
- Have the client specify which fields to return, and return only those fields
- Use the above to allow for expanding nested objects when needed
- Specify an API schema somehow.
All GraphQL does is formalize these things into a specification. In my experience the conditional field inclusion is one of the most powerful features. I can simply create a query which contains all of the fields without paying for a performance penalty unless the client actually fetches all those fields simultaneously.
GraphQL queries tend to map rather neatly on ORM queries. Of course you run into the same sort of nonsense you get with ORMS, such as the n+1 one problem and whatnot. The same sort of tools for fixing those issues are available since your graphql query is just going to call the ORM in any case, with one large addition. Introspecting graphql queries is much easier than ORM or SQL queries. I can avoid n+1 problems by seeing if the query is going to look up a nested object and prefetch it. With an ORM I've yet to see one which allows you to do that.
Lastly GraphQL allows you to break up your API very smartly. Just because some object is nested in another doesn't mean they are nested in source code. One object type simply refers to another object type. If an object has some nested objects that needs query optimizing you can stick that optimization in a single place and stop worrying about it. All the objects referring to it will benefit from the optimization without knowing about it.
GraphQL combines all of the above rather smartly by having your entire API declared as (more or less) a single object. That only works because queries only run if you actually ask for the relevant fields to be returned. It's very elegant if you ask me!
Long story short: yes you run into the same sort of issues optimization wise you get with an ORM, but importantly they don't stack on top the problems your ORM is causing already.
And when you are explicit about how you want to implement joins etc, you pretty much have to hand code the join anyway, so I don't see the point.
In almost all use cases that I've come across, a standard HTTP endpoint with properly selected parameters works just as well as a GraphQL endpoint, without the overhead of parsing/dealing with GraphQL.
Maybe it's just that the backend devs in my last project werenyevery good, but the backend GraphQL code was ridiculously complex and impossible to reason about.
GraphQL has been around for years and people keep making this argument, but where are all the horror stories of unbounded queries being made and systems being hobbled? The argument is beginning to sound anemic.
A decent system will provide the hooks you need to hand optimize certain cases somehow, but There are always limitations and hoops to jump through and additional complexity to manage. The extra layers that are meant to make your life easier are getting in the way instead. (May or may not still be worth it, but the point is, it’s not a foregone conclusion.)
And, of course, an optimizer will handle different SQL queries differently.
...and other lies we tell ourselves to sleep soundly at night.
But just like ORMs, they do work for the simple cases which tend to abound and you can hand-optimize the rest.
So tell me how the query optimizer could possibly tell the difference between generated and hand-written SQL?
Hand-written SQL gives the author a chance to be more precise in its needs, not only in the SQL but also by pre-generating obvious indexes to be performant from the get-go.
And the whole point of GraphQL is that you specify exactly what data you need, so overfetching is avoided. This is in contrast to traditional REST API's where you get a fixed resource.
If present. And for that they need to be created, and which are the right ones is less obvious when working higher in the abstraction staircase.
> It doesn't make a difference. How could it?
Because the SQL generated by ORMs can be wildly stupid in many cases. Here's one blogpost with an example, where regular SQL and query builder that maps to SQL almost directly generate a decent query on a simple relation, while a full ORM does something stupid: https://blog.logrocket.com/why-you-should-avoid-orms-with-ex...
GraphQL allows you to specify what data you need. Obviously, if you use some middleware which throws this information away and just fetches everything from the database, then you have an inefficient system. But this is not a problem inherent to GraphQL or generated SQL in general.
Their justification for needing it is that the API team takes too long to implement changes, and endpoints never give them the data shape they need.
The silent reason is that server-side code, databases, and security are a big scary unknown they are too lazy to learn.
A big project cannot afford to ask for high standards from frontenders. You need a hoard of cheap labor to crank out semi-disposable UIs.
Carefully designed endpoints require a lot of back and forth between teams with very different skill set so to build them you need to plan in advice, but it does not work here where you literally have to move fast (and fail fast!).
You only have two options left: (1) you either ask frontenders (or UX-wise devs) to do the whole thing or (2) you build an abstraction layer that let frontenders query arbitrarily complex data structures with near-perfect performances (YMMV).
In case (1) you’re looking for REAL full-stacks, and it’s not that easy to find such talented developers. In case (2) well… that’s GraphQL.
I've certainly seen timing issues between frontend and backed teams; actually, I don't think I've ever been on a project where that wasn't an issue!
But on my last project, which had a GraphQL backend, this was still a problem. The backend integrated with several upstream databases and REST APIs, so the backend team had to build the queries and implementations before the frontend could do anything. At least with REST they would have been able to mock out some JSON more easily.
It seems like exactly the ORM solution/problem but even more abstract and less under control since it pushes the orm out to browser clients and the frontend devs.
ORM suffer from being at beyond arms length from the query analyzer in the database server.
https://en.wikipedia.org/wiki/Query_optimization
A query optimizer that's been tuned over decades by pretty serious people.
Bad queries, overfetching, sudden performance cliffs everywhere.
Graphql actually adds another query language on top of the normal orm problem. (Maybe the answer is that graphql is so simple by design that it has no dark corners but that seems like a matter of mathematical proof that I haven't seen alluded to).
Why is graphql not going to have exactly this problem as we see people actually start to work seriously with it?
Four or five implementations in javascript, haskell and now go. From what I could see none of them were mentioning query optimization as an aspiration.