Queries in GraphQL are analogous to REST’s GET and they allow to ask the server for the data we need. Contrary to REST however, we get full power to ask for exactly what we need and in the shape we need it.
For this post we’ll run queries against a Star Wars public GraphQL API endpoint. See this Github repo for a list of public GraphQL endpoints that can be really useful to practice your GraphQL querying skills. You’ll notice that endpoints come with a tool called GraphiQL that makes it very easy to run queries. GraphiQL offers autocompletion and very useful error messages (thanks to the fact that GraphQL is a typed query language.)
A Basic Query
We can query for simple scalar types (Int, Float, String, Boolean or ID), which act as primitives, or we can query for objects. Notice how, when querying for objects, we need to start a new selection set:
{ allFilms { films { title } } }
Our films field is an object of type Film so we open a new selection set and in it we get the title field which is of type String
And here’s the data we get back:
{ "data": { "allFilms": { "films": [ { "title": "A New Hope" }, { "title": "The Empire Strikes Back" }, { "title": "Return of the Jedi" }, ...
Named Queries
In the previous example, our query is anonymous. Much like with anonymous vs named functions in JavaScript, it’s often very useful to name our queries to later help with logging and debugging. Here’s the same query as above, this time with a name:
query GetTitles { allFilms { films { title } } }
Comments
You can add comments in queries with the # character:
query GetTitles { allFilms { films { # Getting film titles title } } }
Arguments
In many cases, you’ll need to pass arguments to indicate which entry to get:
query GetReturnOfTheJedi { film(id: "ZmlsbXM6Mw==") { title director releaseDate } }
The response:
{ "data": { "film": { "title": "Return of the Jedi", "director": "Richard Marquand", "releaseDate": "1983-05-25" } } }
Variables
Instead of providing literal values directly as arguments, we can use variables. To do this, we specify a variable name prepended by $ with the query name along with the variable type, and then on the field itself we pass the variable name instead of the literal value:
query GetReturnOfTheJedi($id: ID) { film(id: $id) { title director releaseDate } }
We would pass-in the variable value like this, where filmId is a variable in our program that contains the id to lookup:
{ "id": filmId }
Aliases
Sometimes a field name on the server doesn’t match the shape we want for the returned data. You can easily fix that with aliases:
query GetTitles { allFilms { films { filmTitle: title } } }
And the returned data will look like this:
{ "data": { "allFilms": { "films": [ { "filmTitle": "A New Hope" }, { "filmTitle": "The Empire Strikes Back" }, { "filmTitle": "Return of the Jedi" }, ...
Directives
There are two directives that come built-in with the GraphQL spec: @skip and @include, and they allow to filter returned data.
Let’s say that we’re starting with this passed-in as a query variable:
{"includeDirector" : true }
@skip
Use @skip to omit data if a passed-in value is true:
query GetTitles($includeDirector: Boolean!) { allFilms { films { filmTitle: title director @skip(if: $includeDirector) } } }
The response:
{ "data": { "allFilms": { "films": [ { "filmTitle": "A New Hope" }, { "filmTitle": "The Empire Strikes Back" }, { "filmTitle": "Return of the Jedi" }, ...
Notice the ! after the Boolean type. This is used to indicate that the variable is required.
@include
Use @include for the opposite, to include data only if a passed-in value returns true:
query GetTitles($includeDirector: Boolean!) { allFilms { films { filmTitle: title director @include(if: $includeDirector) } } }
The response:
{ "data": { "allFilms": { "films": [ { "filmTitle": "A New Hope", "director": "George Lucas" }, { "filmTitle": "The Empire Strikes Back", "director": "Irvin Kershner" }, { "filmTitle": "Return of the Jedi", "director": "Richard Marquand" }, ...
Fragments
Many times, query fields would be repeated unnecessarily, like in this example:
query GetFilmInfo { film1: film(id: "ZmlsbXM6NA==") { title director producers } film2: film(id: "ZmlsbXM6Ng==") { title director producers } }
In these cases, fragments come to the rescue:
query GetFilmInfo { film1: film(id: "ZmlsbXM6NA==") { ...info } film2: film(id: "ZmlsbXM6Ng==") { ...info } } fragment info on Film { title director producers }
Notice how we spread our info fragment into our query to get the same result. Notice also how the fragment itself needs to be defined on a specific type. This allows for correct type checking across the board.
Inline Fragments
Inline fragments are a type of fragment defined as part of your queries. They become especially useful to deal with data that returns either an interface or an union type. They allow to deal with data differently depending on its type.
For this last example, let’s use an hypothetical example where a field entry can by of either type Post or Page:
query GetEntry($id: ID!) { entry(id: $id) { title ... on Post { keywords lastRevision } ... on Page { content publishedDate } } }
If our returned entry is of type Post, we’ll get its title, keywords and lastRevision, and if it’s of type Page, we’ll get the title, content and published date.
Learning More
Refer to the official GraphQL documentation to dive deeper.