Measuring the Cost of a GraphQL Query with mercurius-explain
The development experience with GraphQL makes for a simple and immediate way to access data. Working on the backend means focusing on how and where the data is obtained from, whereas the frontend focuses on retrieving the data necessary for computation and display.
This freedom of action, however, hides some possible performance problems.
Fragments let you construct sets of fields, and then include them in queries where they’re needed. Developers tend to centralise the fragments definitions and use them in each query where the entity is used, even if just a subset of the attributes is necessary.
Often a client requests unnecessary data in an API call with the assumption that the extra fields are free. Instead, they are the cause of extra queries, sometimes even expensive ones. Not knowing the cost of a query makes it impossible to perform optimisation correctly.
In this article, we show how to use the mercurius-explain plugin to monitor the performance of your GraphQL APIs by tracking the behaviour of your resolvers.
Let’s use this mercurius application to set up a simple GraphQL server:
Open the GraphiQL interface at http://localhost:3000/graphiql and test the query:
In case of a slow query, the overall response time wouldn’t be enough to identify the source of the slowdown, because the query would look like a black box to the user.
How can we discover the real cost of a GraphQL query?
Introducing mercurius-explain: a Plugin for Query Profiling
mercurius-explain is a simple and lightweight Mercurius plugin that records how many times a GraphQL resolver is invoked and how long it took to retrieve the data, which helps to keep track of performance changes during development and to troubleshoot possible bottlenecks.
Fastify makes it easy to register Mercurius plugins to extend the functionality of the server and interact with the GraphQL adapter.
In the example below, we have included some boilerplate code for instantiating a new Fastify server and registering the mercurius-explain plugin.
Once enabled, the mercurius-explain plugin takes advantage of the extensions field, natively supported by Mercurius, to inject the
explain object into the response of the GraphQL API.
Two properties compose the
profiler collects runtime performance metrics and shows the execution time of each resolver. It records
end and execution
time for each GraphQL resolver.
process.hrtime(), we get nanosecond precision, allowing us to measure even the smallest performance change.
resolverCalls property keeps track of the times a resolver is invoked during the execution of the query.
mercurius-explain also comes with a GraphiQL plugin that displays performance data within GraphiQL itself: mercurius-explain-graphiql-plugin.
mercurius-explain-graphiql-plugin is a GraphiQL plugin supported by mercurius and can be enabled by adding
explainGraphiQLPlugin in the
graphiql settings, as in the example above.
It provides a simple yet effective interface to visualise the performance of GraphQL API resolvers.
The picture below shows the
profiler report of a query:
It highlights the most time-consuming operations depending on their impact on the query.
💡 Note that the total time is not equal to the sum of each resolver because resolvers are executed concurrently
The picture below shows the data contained in
Who can access the report?
mercurius-explain can be used in production because it allows fine-tuned access control.
enabled option also accepts a function, which can be used to conditionally enable the plugin:
In this way, only requests sent by a hypothetical admin user and with a specific request header will receive the
explain field in the response body.
Any instrumentation slows down the observed system’s performance, but mercurius-explain has a very limited footprint on performance so it is suitable to be used in production. Benchmarks are available in the repository on GitHub.
In our experience, adopting GraphQL with the right tooling can provide significant improvements to developer experience running complex APIs.
If you’re looking to maximise your GraphQL APIs performance on mercurius, mercurius-explain is a simple, lightweight and powerful option to consider.