Executing Queries

Now that we have defined the schema and breathed life into it with our resolver functions, we can execute arbitrary query against the schema.

The graphql package provides the graphql.graphql() function to execute queries. This is the main feature of GraphQL-core.

Note however that this function is actually a coroutine intended to be used in asynchronous code running in an event loop.

Here is one way to use it:

import asyncio
from graphql import graphql

async def query_artoo():
    result = await graphql(schema, """
    {
      droid(id: "2001") {
        name
        primaryFunction
      }
    }
    """)
    print(result)

asyncio.run(query_artoo())

In our query, we asked for the droid with the id 2001, which is R2-D2, and its primary function, Astromech. When everything has been implemented correctly as shown above, you should get the expected result:

ExecutionResult(
    data={'droid': {'name': 'R2-D2', 'primaryFunction': 'Astromech'}},
    errors=None)

The ExecutionResult has a data attribute with the actual result, and an errors attribute with a list of errors if there were any.

If all your resolvers work synchronously, as in our case, you can also use the graphql.graphql_sync() function to query the result in ordinary synchronous code:

from graphql import graphql_sync

result = graphql_sync(schema, """
  query FetchHuman($id: String!) {
    human(id: $id) {
      name
      homePlanet
    }
  }
  """, variable_values={'id': '1000'})
print(result)

Here we asked for the human with the id 1000, Luke Skywalker, and his home planet, Tatooine. So the output of the code above is:

ExecutionResult(
    data={'human': {'name': 'Luke Skywalker', 'homePlanet': 'Tatooine'}},
    errors=None)

Let’s see what happens when we make a mistake in the query, by querying a non-existing homeTown field:

result = graphql_sync(schema, """
    {
      human(id: "1000") {
        name
        homePlace
      }
    }
    """)
print(result)

You will get the following result as output:

ExecutionResult(data=None, errors=[GraphQLError(
    "Cannot query field 'homePlace' on type 'Human'. Did you mean 'homePlanet'?",
    locations=[SourceLocation(line=5, column=9)])])

This is very helpful. Not only do we get the exact location of the mistake in the query, but also a suggestion for correcting the bad field name.

GraphQL also allows to request the meta field __typename. We can use this to verify that the hero of “The Empire Strikes Back” episode is Luke Skywalker and that he is in fact a human:

result = graphql_sync(schema, """
    {
      hero(episode: EMPIRE) {
        __typename
        name
      }
    }
    """)
print(result)

This gives the following output:

ExecutionResult(
    data={'hero': {'__typename': 'Human', 'name': 'Luke Skywalker'}},
    errors=None)

Finally, let’s see what happens when we try to access the secret backstory of our hero:

result = graphql_sync(schema, """
    {
      hero(episode: EMPIRE) {
        name
        secretBackstory
      }
    }
    """)
print(result)

While we get the name of the hero, the secret backstory fields remains empty, since its resolver function raises an error. However, we get the error that has been raised by the resolver in the errors attribute of the result:

ExecutionResult(
    data={'hero': {'name': 'Luke Skywalker', 'secretBackstory': None}},
    errors=[GraphQLError('secretBackstory is secret.',
            locations=[SourceLocation(line=5, column=9)],
            path=['hero', 'secretBackstory'])])