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:
{
person {
name
age
picture
}
}
{
"person": {
"name": {},
"age": {},
"picture": {}
}
}
Arguments become an object under __arguments
field:
{
person(id: 100) {
name
age
picture
}
}
{
"person": {
"name": {},
"age": {},
"picture": {},
"__arguments": {
"id": 100
}
}
}
Variable references are converted into string values:
{
person(id: $personId) {
name
age
picture
}
}
{
"person": {
"name": {},
"age": {},
"picture": {},
"__arguments": {
"id": "$personId"
}
}
}
And directives become an array under __directives
field:
{
person(id: 100) {
name
age
picture @skip(if: $lowBandwidth)
}
}
{
"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:
{
stationWithEvaId(evaId:8000105)
{
name
}
}
{
"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.