This page contains examples of some of the basic ways to use queries and mutations, for a much more thorough explanation of all of these, and coverage of more advanced usage, see the Queries and Mutations page on the official GraphQL site.
I have left the GraphiQL interface enabled on /graphql so you can use that to interact directly with the middleware layer.
A nice simple request which takes no parameters and has a fixed return type.
This call takes in a single String but still has no flexibility in what is returned and so does not need any return fields specifying.
If you look at the query, you can see that value you input gets wrapped in quotes as the query expects the value to be a String, see what happens if you put a quote in your input value.
The previous call passes the input variables inline in the query, there is however a second way to do this.
Input values can be extracted out and passed separate from the query in their own segment of the request called Variables. This is the preferred way of making calls as it separates query and values and so acts in a similar way to parameterised queries in SQL.
The call made here is done to the same endpoint as the previous one, the only difference is the separation of the values.
Same as above but this time passing in an integer rather than a string. I've included this one to show the extra information included in the error when the middleware expects an integer but gets something else.
Try passing in a string, remember, a string is wrapped in quotes in the variables.
Up to now, all input parameters have been defined as mandatory, this is done by adding a ! to the end of the parameter type, e.g. String!, without the ! the parameter becomes optional.
This query takes two optional parameters, you can use the checkboxes to specify which you want the call to pass.
I've implemented two versions of the call, one with the parameters inline and one with them as variables. Tell the site you only want to pass one of the variables but then see if you can find a way to inject the other. It is possible with the parameters inline but should not be when using variables.
Note that in this implementation, hanging commas on the end of the input parameter list are allowed, I do not know if this is the case for all implementations.
As with other languages, GraphQL has the concept of enums. This allows the query to specify a fixed number of values that can be passed to a parameter, passing something outside this range will result in an error.
Note the lack of quotes around the value passed to the fruit parameter, if you try to wrap it in quotes you will be passing a String, not a Fruit, and so the request will be rejected.
Also note that the value is case sensitive, try passing a lowercase value in and you should see a helpful error asking if you really meant something else.
As well as being able to return primitive types, String, Int etc, there is also the ability to return more complex types in the form of objects.
This query returns a BasicObject which is made up of two strings and an integer. The query called here only requests the two strings, try to modify it to also request the integer value.
Remember, if the interface is only expecting the two strings, so will not display any extra data, you will have to look behind the scenes to see it.
As well as being able to return objects, it is also possible to pass objects in. From what I've seen, this is not used much in queries, more in mutations, but seeing as it is possible, here is an example.
With other API systems, such as REST of SOAP, if you want to make multiple queries you have to make multiple requests. With GraphQL you can combine multiple queries into a single request.
The queries do not have to share inputs or outputs. One of the uses I can think of for this is loading content to fill a number of combo boxes and other input elements on a page, one call can pull back all the values at once rather than having to make a request for each element.
This request combines "String Parameter, String Returned", "String Parameter, Object Returned" and "Object Parameter, Object Returned" into a single request.
Queries are requests for data, mutations are requests to make changes to data. If you know CRUD, queries are the R(ead), mutations are the C(reate), U(pdate) and D(elete).
From what I've read, there is nothing to stop a query from being used to update data, only convention. The only technical difference is that if you pass multiple mutations in a single request they are ran serially as opposed to queries which are ran in parallel. This is to prevent race conditions when updating data.
The format is the same as for queries, only the definition on the server marks them as different.
In this example, a number is passed in and is stored on the server, from what I've read, the convention is to return the value passed in to confirm it was set, that is what the setInt mutation does, it also returns a nicely formatted comment saying what it has done.
You can check the mutation worked by using the getInt query to retrive the currently stored value.
As everyone is using the same server, if you find the value you are setting is not being returned as you would expect, it might be someone else working on the lab at the same time.
This is probably not universal across all GraphQL frameworks, but the NodeJS one used for this project gives very verbose error messages. The following examples use these to enumerate a previous unknown query.
The best bit about these verbose error messages is that if you make a mistake in a query, but it is almost correct, the error will contain suggestions to try to help you fix your mistake. This can be used to aid the guessing or brute forcing of valid values. This does not help if the names are obscure, but if they are common words, or you can work out a style or format based on other calls, then it should be possible to piece together at least some values.
This is especially useful when introspection is disabled and so you have to work out, or simply guess, how the system is put together.
For more information on this, see Validation on the GraphQL site.
In this example, we are going to extract some user data from the system. A good first guess is that any related queries will probably have the word user in them, so make a call to the "user" query and see what happens.
As can be seen in the response, the system helpfully gives us the "getUser" function name as suggestion to fix our mistake. Let's make a call to "getUser" and see what comes back next.
Two errors are returned, one covering the missing argument, one the returned subfields.
The error about the missing argument tells us that we have to provide an argument with the name of "name" and it should be of type "String!". Let's test that.
Looks good, that just leaves the subfields. As with the query names, if you make a guess, and you get close, the system will make suggestions of valid values for you to try. Something I noticed is that it is sometimes possible to get quick wins by throwing the alphabet into the list of subfields and looking what comes back.
I've missed a few letters out here, but as you can see, there is one hit, the subfield name "id". We can confirm that by requesting it.
And there we go, we've gone from knowing nothing about user functions to generating a valid query and returning data. The next step for this would be to brute force some other subfield names using a dictionary of words related to users, both of which would probably throw up some more fields.
You could also do some manual guessing based on the application, for example, if the site shows a user with a phone number and email address, then you could probably guess a couple of other fields based on those.
Lab created by Robin Wood - DigiNinja