Show HN: Quiver – GraphQL on Steroids
medium.com
medium.com
It does bring up something that I think the GraphQL community hasn't really addressed well yet though... Part of the way Quiver works is that it only allows pre-defined queries which can then be compiled ahead of time.
> Only allow certain queries from a document store.
This is becoming a common response for how to make GraphQL performant and "secure". [1]
But this solution only works for internal APIs. If you're trying to expose a public GraphQL API it isn't viable.
Not only that, but to sync these "persisted" queries from the client to the server, lots of the persisted query tools require you to hard-code GraphQL queries as `.graphql` files in your repository... effectively re-inventing hard-coded REST responses. Except with an extra build process between the client and the server for each new deploy. You no longer really have each component requesting exactly what it needs.
The more research I do into GraphQL, the more it seems like the promise of "query anything" that libraries are selling is often discarded when you get to actual implementation. And you end up with a situation that is much more similar to REST/HTTP than originally intended.
[1]: https://dev-blog.apollodata.com/persisted-graphql-queries-wi...
The most important thing about the persisted queries approach highlighted in that article, compared to traditional REST architecture, is that the queries live in your _client_ codebase. So the frontend developers write the shape of the data, rather than backend folks hardcoding it in like with endpoints.
I would say that at the current moment only a small minority of GraphQL users (based on our experience) are using persisted queries, and running in production without that is just fine with good performance and security. A simple approach like query timeouts, or discarding queries based on complexity, goes a long way here.
I agree, having queries live in the client is a great goal for DX on the frontend. The ideal being to have them co-located right with the components that need the data. But the issue is that these kinds of solutions are often presented without discussing their tradeoffs. From the article:
> Because persisted queries are static by definition, they also give you the possibility of optimizing execution on the server for specific queries, for example by hand-crafting a highly efficient database query.
Well, that's a bit harder when the client controls the queries and they're automatically "recompiled" on each new deploy, because now you're optimizing a moving target that could change dramatically with little notice. It's not really a benefit, since it's such a fragile situation to be relying on in the first place.
Persisted queries also kind of make co-location of queries with components a non-starter. Maybe Apollo doesn't want to allow co-located queries, which could be an okay decision, but isn't really discussed much as a tradeoff.
And then it doesn't solve the larger issue that these solutions don't work for public-facing APIs. So you end up having to solve the same issues (eg. bandwidth, security, caching) again in an entirely new way.
I think it's something the community will need to address if public-facing GraphQL APIs are going to flourish.
I don’t understand your concerns about performance or security. GraphQL should only expose the fields you can fetch in a fast and safe way. Optimise the field lookups; not the whole query execution.
A deeply nested GraphQL request is no worse than a series of REST fetches.
We’re using Juniper in production as a central API for other internal services.
The performance of literally any other GraphQL server is just hilariously slow compared to it.
We previously used Node GraphQL and were happy with it, but at some point we needed more raw speed.
(edit: just to make it clear -- I'm not affiliated with this project)
> The performance of literally any other GraphQL server is just hilariously slow compared to it.
Is the same kind of fast as in getting the data from the sql server takes half a second, but x then renders the response in 20 ms not a slow 80 ms (520 vs 580 ms response time to the client)?
I mean, I unless your data is in an in-memory in-process database, it comes from the network to the graphql server, which acts as a proxy for the client?
Which makes a claim of being a lot faster than eg nodejs interesting - but also somewhat surprising.
For most GraphQL API requirements in terms of performance there's no real value in using Rust if you're not already very proficient in it.
That said, 520ms is extremely pessimistic for most GraphQL servers.
The API to a project I'm running is built using the Node / Apollo GraphQL server and its response times are well under 90ms; that's with a CDN in between: https://api.psychonautwiki.org. [server in Germany; US mileage may vary]
(btw -- I'm not affiliated with Juniper)
As for the 500ms I wasn't thinking only network overhead, but also things like complex sql queries with lots of joins etc. And maybe on top of that going through a json-rest/soap endpoint:
Client > graphql server > rest endpoint 1..3 > [sql server, document db, key-value store]
Where the graphql server takes a query, fetches and merges resources, and sends the result back to the client. (and as you mention, maybe rest and rest are external services)
If it's just:
Client > graphql > documentdb
(since your link mentions wiki...) - I'm not sure I see that graphql is a great benefit (but I'm perfectly open to that being wrong; as I said, I've only just begun looking at graphql).
And you're very right that GraphQL isn't very smart for a wiki (besides being a fancy replacement for REST).
PsychonautWiki is documenting the effects (and dosage, durations, ...) of hallucinogenic substances; GraphQL allows people to easily query for substances with similar effects, or substances from the same class, .., all in a single request. Also you get type checking and field selection for free.
There are some major benefits of this: - improving the speed of the HTTP request (it always be faster to just send a documentId rather than a big query sting from the client side) - Control what is queried against your API (so there are no "Facebook" clones with some extra views)
At the end, with enough tooling you can make the frontend projects upload their documents queries automatically as their developers work on it. So the tradeoffs for GraphQL APIs focused on internal products are not as bad.
Quiver will compile the GraphQL query code to runtime code. Is a similar approach of what v8 does when executing JS (it compiles it to assembly code), or what modern template engines do to speed up their rendering.
So basically, it will transform the GraphQL query into a pure function that can be executed without the overhead of having the GraphQL engine doing the calculations at runtime.
Syrus is correct that it will have about the same performance as Node in these particular benchmarks.
We are working on performance improvements for Apollo Server 2.0, but they are targeted at a totally different use case. Our experience working with hundreds of companies using GraphQL in production has indicated that the CPU execution time of the query is almost never the bottleneck when it comes to GraphQL.
The real thing you want to watch out for is the time it takes to call underlying APIs and databases, which will absolutely dwarf the actual time spent in the CPU. In this case, Node's non-blocking architecture helps a ton, and taking any action that prevents repeated calls (like basic caching) is critical.
In AS 2, we're going to introduce some features that make caching underlying data calls even easier, which we think is going to have the biggest impact on people's response time with GraphQL.
It's the same kind of thing as Express.js competitors showing that their routing logic handles 50k requests per second instead of Express's 25k, but only for "simple" use cases—AKA ones that don't involve a database. You should almost never be evaluating a server on these parameters.
However if you think it will be useful to compare against, I will be super happy to accept it as a PR in gbench: https://github.com/graphql-quiver/gbench