Load testing GraphQL with k6

By Pepe Cano. In API Testing. On 2019-09-20

The popularity and adoption of GraphQL have been impressive in the last years, and as more users start discovering k6, we are getting more frequent questions about GraphQL. This post provides some useful information for those beginning with k6 to test a GraphQL service.

Table of contents

Does k6 support testing GraphQL?

The short answer is YES. k6 supports testing GraphQL services running over HTTP or WebSocket.

But I'd like to elaborate more on this answer because we've found some users were slightly confused about how GraphQL or k6 works.

GraphQL is a transport agnostic "language"

GraphQL is a language for query and manipulation of APIs; the specification defines the syntax to communicate with a GraphQL API.

For example, given an API schema like:

type Query {
  me: User
}

type User {
  id: ID
  name: String
}

The query will be:

{
  me {
    name
  }
}

GraphQL is transport-layer agnostic; it does not restrict a GraphQL server to run over a particular transport protocol. HTTP is the common choice because of its ubiquity, but a GraphQL server could run over different protocols like:

  • HTTP, AMQP, Websockets...
  • Low level-protocols: TCP, UDP, gRPC…

On the other side, k6, which takes the role of the GraphQL client, can generate requests over HTTP or Websocket at the time of writing this post. This means that k6 can load test any GraphQL server that runs over one of the supported k6 protocols.

Example testing the GitHub GraphQL API

Let's try to show quickly an example using k6 to test a GraphQL service. To avoid setup and run our own GraphQL server, we will load test the GitHub GraphQL API. We chose the GitHub API because it is familiar to developers, well-documented, and have multiple schema types, queries, and mutations to play with.

To start, we need a Personal GitHub Token to authenticate with the GitHub API. Follow this guide to create a Personal Token for running this test. Depending on the requests of our test, your token will need different permissions or scopes. For the next example, your token needs the following permissions:

  • Access public repositories: repo:public_repo
  • Read and write team discussions: write:discussion

How do I create my first GraphQL request?

Because the GitHub GraphQL API runs over HTTP, and the GraphQL queries or mutations requires a JSON-encoded body, you will use the http.post function to generate requests to hit the API.

We could start our test getting some data from the GitHub API:

Which was the first GitHub issue of the k6 project?

The code is quite simple; you only have to set your Token on the Authorization header, write the GraphQL query (Object/Repository/Issues) and send it on the POST request.

let accessToken = "YOUR_GITHUB_ACCESS_TOKEN";

let query = `
 query FindFirstIssue {
   repository(owner:"loadimpact", name:"k6") {
     issues(first:1) {
       edges {
         node {
           id
           number
           title
         }
       }
     }
   }
 }`;

let headers = {
 'Authorization': `Bearer ${accessToken}`,
 "Content-Type": "application/json"
};

let res = http.post("https://api.github.com/graphql",
 JSON.stringify({ query: query }),
 {headers: headers}
);

Right now, our k6 script is just hitting the endpoint to request always the same data, but we wanted to create a test to replicate a particular user interaction.

Debugging the response data

While developing your script, you might want to see the response content to validate the data or use it in your load test.

if (res.status === 200) {
  console.log(JSON.stringify(res.body));
  let body = JSON.parse(res.body);
  let issue = body.data.repository.issues.edges[0].node;
  console.log(issue.id, issue.number, issue.title);
}

The output will be something like:

INFO[0001] "{\"data\":{\"repository\":{\"issues\":{\"edges\":[{\"node\":{\"id\":\"MDU6SXNzdWUxNzA0MzczOTY=\",\"number\":4,\"title\":\"Deduplicate HTTP client code\"}}]}}}}" 
INFO[0001] MDU6SXNzdWUxNzA0MzczOTY=                      0=4 1="Deduplicate HTTP client code"

Run a mutation to add a 🎉 Reaction to the first k6 issue

This simple example intends to show how to update server data based on data fetched from another request. In GraphQL, you will use mutations to modify data.

You can read the addReaction docs or find examples to learn the syntax of this mutation. In this case, the addReaction mutation needs the issue.id to add the 🎉 Reaction to the issue. Below is how it looks like:

let mutation = `
  mutation AddReactionToIssue {
    addReaction(input:{subjectId:"${issue.id}",content:HOORAY}) {
      reaction {
        content
      }
      subject {
        id
      }
    }
}`;

res = http.post("https://api.github.com/graphql",
  JSON.stringify({query: mutation}),
  {headers: headers}
);

Now, you can run the test and check out the reaction to the k6 issue by hovering over 🎉. Check out the full example.

easygraphql-load-tester

It was a pleasant surprise when we found easygraphql-load-tester to be using k6. Finding users using your product, especially in unexpected and useful ways, is encouraging and a great way to learn about our user needs.

easygraphql-load-tester is a cool idea; it's a node library to make load testing GraphQL as simple as possible. You only have to pass your GraphQL schema (object types and queries), and it'll create a load test using your schema queries.

const userSchema = fs.readFileSync(path.join(__dirname, 'schema', 'user.gql'), 'utf8')
const familySchema = fs.readFileSync(path.join(__dirname, 'schema', 'family.gql'), 'utf8')

const easyGraphQLLoadTester = new EasyGraphQLLoadTester([userSchema, familySchema]);

easyGraphQLLoadTester.k6('k6.js')
// or use
easyGraphQLLoadTester.artillery();

Reusing schema files is an intelligent and rapid way to have a load test for your GraphQL backend up and running without even writing a single GraphQL query. Plus, the library gives you the ability to choose either artillery or k6 😉 to run the load test.

We found easygraphql-load-tester to be very useful if you want a quick way to create simple load tests hitting your GraphQL queries.


The library is not limited to auto-generate a load test based on your schema queries. You can also customize your load test requests selecting or generating custom queries, as well as use mutations. If you want to know more about these features, check out the documentation.

Simple testing is better than no testing

However, easygraphql-load-tester cannot create more realistic tests where one request needs some information from a previous one. Back to the beginnings of k6, we wrote about this topic in our Developer-centric manifesto: "Simple testing is better than no testing".

A unit load test is testing a single unit, like an API endpoint, in isolation.
A scenario load test is for testing a real-world flow of interactions.

easygraphql-load-tester fits into the “unit load” type of testing, but as many other testing tools, it cannot create real-world scenarios like:

  • login and get your user token.
  • use the token to list your items.
  • make an action on item.

We have provided above an k6 example for this type of testing and we recommend you using k6 for that.

Conclusion

We are getting more frequent questions about GraphQL:

We tried to clarify some of the above questions in this post. To recap, Testing GraphQL with k6 is as simple as generating a request with data.

The post also includes a basic example to give a starting point for testing a GraphQL service with k6. We wanted to keep it short, but the possibilities are endless, the k6 script and the k6 options allow running a wide range of different type of performance tests.

Loading...