Vladimir Dyuzhev, author of MockMotor

Vladimir Dyuzhev
MockMotor Creator

How to Mock a GraphQL Service


MockMotor converts GraphQL queries into JSON that scripts can understand.

GraphQL is not JSON

While GraphQL resembles JSON, it is very different.

At first glance, GraphQL is just JSON without values.

{
  person {
    name
    age
    picture
  }
}

However, GraphQL can be much more complex than that.

It can include arguments that narrow down the set of returned objects:

{
  person(id: 100) {
    name
    age
    picture
  }
}

The arguments (and not only arguments) can be passed as variables:

{
  person(id: $personId) {
    name
    age
    picture
  }
}

It can also include directives that are processed by the service and can affect the result in non-trivial ways:

{
  person(id: 100) {
    name
    age
    picture @skip(if: $lowBandwidth)
  }
}

Lastly, the query can contain types, interfaces, enums and unions definitions, schema extensions and other tricky things.

Trying to make sense of all it with string operations that JS provides is a sure way to madness.

What MockMotor offers instead?

GraphQL to JSON Conversion

MockMotor supports GraphQL by converting the query into an equivalent JSON object and placing it into graphql variable.

Query fields become JSON fields:

GraphQL query
{
  person {
    name
    age
    picture
  }
}
graphql variable
{
  "person": {
    "name": {},
    "age": {},
    "picture": {}
  }
}

Arguments become an object under __arguments field:

GraphQL query
{
  person(id: 100) {
    name
    age
    picture
  }
}
graphql variable
{
  "person": {
    "name": {},
    "age": {},
    "picture": {},
    "__arguments": { 
      "id": 100
    }
  }
}

Variable references are converted into string values:

GraphQL query
{
  person(id: $personId) {
    name
    age
    picture
  }
}
graphql variable
{
  "person": {
    "name": {},
    "age": {},
    "picture": {},
      "__arguments": { 
        "id": "$personId"
      }
  }
}

And directives become an array under __directives field:

GraphQL query
{
  person(id: 100) {
    name
    age
    picture @skip(if: $lowBandwidth)
  }
}
graphql variable
{
  "person": {
    "name": {},
    "age": {},
    "picture": {
      "__directives": [
        { 
          "name": "@skip",
          "__arguments": {
            "if": "$lowBandwidth"
          }
        }
      ]
    },
    "__arguments": { 
      "id": 100
    }
  }
}

There are other conversions, such as __fragmentspread, but the above examples should be enough to understand the idea: where we had GraphQL syntax, we now have JSON and can use our usual JS scripts to match and execute a reaction.

You Still Need to Point at the Query Location

GraphQL can appear in multiple places in an HTTP request. It can come as an HTTP parameter, a text value of a JSON field, or constitute the whole request payload.

MockMotor can’t easily tell if the request contains any GraphQL and, if yes, where it is.

Checking all usual places doesn’t guarantee the result and has a large performance impact.

Therefore, you will need to tell MockMotor the path to GraphQL. Use the reaction field Path in Request under GraphQL section:

For GraphQL coming as HTTP request parameter q, the field would contain http.parameters.q.
For GraphQL in a field in a JSON request, as in the screenshot above, input.query.
For GraphQL that is sent as the raw payload, the field should contain just input.

Once the GraphQL is found and validated, the graphql variable is set to the JSON value that represents this GraphQL.

You can then use it in the script. For example, you can match a reaction using a value from the query:

graphql.person.__arguments.id == 100

Example: Mocking Simple GraphQL Service

Let’s mock a simple GraphQL service.

I searched for public GraphQL APIs quickly and found the Deutschebahn railroad stations catalogue. It would serve as a perfect example!

https://developer.deutschebahn.com/store/apis/info?name=1BahnQL-Free&version=v1&provider=DBOpenData#!/default/get_graphql

A query is very simple and is passed in the HTTP parameter query.

Here’s how to get a station name by ID:

?query={stationWithEvaId(evaId:8000105){name}}

And here’s getting more information about the station: name, coordinates and image URL:

?query={stationWithEvaId(evaId:8000107){name location { latitude longitude} picture {url}}}

The response is a JSON document, like the one below:

{
	"data": {
		"stationWithEvaId": {
			"name": "Freiburg (Breisgau) Hbf",
			"location": {
				"latitude": 47.997697,
				"longitude": 7.84117
			},
			"picture": {
				"url": "https://api.railway-stations.org/photos/de/1893_1.jpg"
			}
		}
	}
}

Frankfurt by ID

The simplest query is to get the station name by its id. Let’s say we need to mock a call for Frankfurt (Main) Hbf (evaId 8000105).

?query={stationWithEvaId(evaId:8000105){name}}

We should create a reaction where we:

Specify that GraphQL is in query HTTP parameter
Specify that the HTTP method is GET
Hardcode match by the ID from the query (using mock accounts is overkill for this simple demo)
Return JSON that contains the station name.

Converting to JSON

Once we hinted MockMotor that the GraphQL query is in http.parameters.query, that query is converted into its equivalent JSON and placed into the graphql variable:

GraphQL query for Frankfurt
{
  stationWithEvaId(evaId:8000105)
  {
    name
  }
}
graphql variable
{
  "stationWithEvaId":
  {
    "name": {},
    "__arguments": {
      "evaId": 8000105
    }
  }
}

Matching the Reaction

Now we can match the reaction using a simple JS script to read the evaId from graphql variable:

graphql.stationWithEvaId.__arguments.evaId==8000105

If it computes as true, the current reaction is executed.

Provide the Response Payload

The rest is the same as in JSON services: provide the payload, set the content-type to application/json, set the delay, if required.

Here is the full reaction for reference:

You can also check the live implementation.

Future Plans for GraphQL Mocks

The GraphQL support in MockMotor is still very basic. There are few intended enhancements in the coming releases.

Create from Schema

In future, MockMotor will parse the GraphQL schema and generate a mock service automatically.

Return Requested Fields

It is annoying to create a separate mock for {name} query if you already have one for {id name}.

In future releases, MockMotor will remove the fields that the client didn’t ask for, reducing the number of required mocks.

Performance Optimizations

Currently, no special performance optimizations are implemented for GraphQL. The performance can improve in a few areas.