Quiver: GraphQL on Steroids

An intro to the fastest GraphQL engine on the market

📈 If you are mostly interested on the benchmarks between different GraphQL servers (Python, JS, Scala, Go…) you can jump directly to the charts at the end 👇

I’ll be frank… I’ve been obsessed with GraphQL performance for the last few years. Aiming to improve it, I created Quiver.
Quiver is a GraphQL engine that focuses on making GraphQL as fast as possible by removing the overhead of the GraphQL runtime.

Quiver not only speeds up GraphQL in Python by a factor of 5–10x but also outperforms any other GraphQL engine available

So, how is Quiver achieving a *blazing-fast speed when executing queries?
First, we need to analyze what happens when we run a GraphQL query.

Steps for executing a GraphQL query

Each time a GraphQL query is executed (e.g. graphql(schema, '{ hello }')), the following happens:

  1. The query string { hello } is parsed into a GraphQL AST.
  2. The GraphQL AST is validated against the schema. This ensures that the query is not just valid, but correct according to the schema. For example, this step checks that the type fields requested are available in the schema.
  3. At runtime, the engine walks through the GraphQL AST and determines the fields requested from the query. An object containing the requested fields filled with data is returned.
  4. The GraphQL view serializes the resulting object into something a client can consume (e.g. JSON).

* I’m intentionally skipping the HTTP transport step for sending the results back to the client.

So, what can we do to speed up the queries?

Having analyzed the different steps, there are some strategies that will help us to optimize the execution speed:

  • Cache the GraphQL AST of a given query, so that we avoid parsing subsequent similar queries.
  • Only allow certain queries from a document store. This has two main implications: being able to control the queries that are executed against our schema, and therefore assume that queries are already validated so we don’t need to check them at runtime.
  • Minimize the runtime logic for the query. A simple approach could be to traverse the GraphQL fields for a specific selection into code, so we don’t need to process this logic at runtime.
    The compilation strategy is something that template engines have been doing for years (Jinja, Hero, …).
While Quiver focuses on the latter, it’s approach also removes the need of parsing and validating the AST at runtime for each query execution.

Quiver is to GraphQL what a compiled language (C, Go, …) is to an interpreted one (Python, Ruby, …).
By analyzing and compiling the query into code, we can achieve substantial speed improvements.

So, how does Quiver work?

Let’s say you want to execute the following Query:

query HelloWorld {
hello
}

Quiver will compile the query into runtime code for your language (depending on the language):

def execute(root, variables=None, operation_name=None):
if operation_name is None or operation_name == 'HelloWorld':
selection_type = schema.get_type('Query')
hello_resolver = selection_type.get_field('hello').resolver
info = ResolveInfo(...)
return {
'hello': hello_resolver(root, info)
}

Because this compilation step happens only once, executing the HelloWorld GraphQL query will be as fast as executing a normal function that returns an object in the language of your choice, while being fully complaint with GraphQL and with no changes required on your GraphQL schema server-side.

Running the HelloWorld query will take only 9us when using Quiver in Python. Yes… that’s 0.009ms

Show me numbers!

When creating a project like this is important to be able to measure it objectively.

With this in mind, and inspired by graphql-bench from Hasura I created gbench, an utility library to help measure the speed of different GraphQL servers.

gbench will automatically start the servers locally, warm them up (when necessary) and benchmark each server against all the queries we indicate in the config file.

Here is a summary of the response time (per server & query) comparing Graphene, Graphene+Quiver, Node, Sangria and Go.

The response time for GraphQL queries across different languages, using 20 concurrent connections

You can also check it in the dedicated website: speed.graphql-quiver.com

Quiver is able to handle up to 100k GraphQL queries per second in a single instance

It was a pleasant surprise to discover that Quiver in Python not only speeds up GraphQL in Python by a factor of 5–10x but also outperforms any other GraphQL engine available (including JS, Scala and even Go) by a great margin.

Ready to try Quiver?

I’m very happy to announce that Quiver is now available to anyone using GraphQL in Python, and will be available in other major languages (JS, Ruby, Go) very soon.

Signup in the Quiver website and follow the instructions there :)
https://graphql-quiver.com/signup/

Using Quiver is as easy as swapping the GraphQL engine provider, preserving your same resolvers, schema and GraphQL code that you already have in place.

Not using Python?

If you are using JS, Go or Ruby, performance can be improved by 3 to 5x using Quiver. Please ping me if you or your company is interested!

Try Quiver and start enjoying *blazing-fast GraphQL queries!