Load testing GraphQL with WRK

1 minute read

For performance testing wrk is one of my favorite tools. Whether you are trying to get a quick benchmark or building a performance test suite - it is fairly simple using wrk.

That is, as long as your requests are fairly simple. To benchmark a GET request against a classic REST API you could run:

wrk -c <user-count> -t <cpu-core-count> -d 10 --latency https://your-server/api/books

Easy as pie, right? But what about less simple requests? Specifically, does wrk work for, say, GraphQL APIs?

While are other tools that you could use (for example k6), wrk does the job well enough - if you aren’t scared of a little Lua scripting.

What you are seeing in this post is pretty much the entirety of all the Lua I’ve written in my entire life. So. Now you know. Take the Lua parts with a grain of salt.

Using Lua to perform GraphQL Queries

You may pass a Lua script to wrk using the -s option.

wrk -s script.lua https://your-server/api/graphql

To perform a GraphQL query the script could look something like this:

wrk.method = "POST"
wrk.headers["Content-Type"] = "application/json"
wrk.headers["Accept"] = "application/json"

query = [[
  query books($ids: [Integer]) {
    books(ids: $ids) {
        id
        name
        price
      }
    }
  }
]]
variables = [[
  "ids": [1,2,3,4]
]]
wrk.body ='{"query": "' .. string.gsub(query, '\n', '') .. '", "variables": {' .. string.gsub(variables, '\n', '') .. '} }'

Even if you have no clue about Lua you can most likely infer what is going on here. A quick explanation:

wrk.method = "POST"
wrk.headers["Content-Type"] = "application/json"
wrk.headers["Accept"] = "application/json"

Most GraphQL servers require specific headers and HTTP verbs when processing requests. If your API has some specific requirements you will need to change this accordingly.

query = [[
  query books($ids: [Integer]) {
    books(ids: $ids) {
        id
        name
        price
      }
    }
  }
]]
variables = [[
  "ids": [1,2,3,4]
]]

Double-brackets ([[...]]) denote multi-line strings in Lua, which allow us to specify our query and variables in a readable way.

wrk.body ='{"query": "' .. string.gsub(query, '\n', '') .. '", "variables": {' .. string.gsub(variables, '\n', '') .. '} }'

This sets the request body - as you might expect. Because the request body must contain valid JSON, we remove any line breaks from our query and variables variables and use string interpolation (..) to insert them into the final payload.

And that’s it. Have fun performance testing your GraphQL APIs! :rocket:

Updated: