The following is the first few sections of a chapter from GraphQL and Android, plus headings for the remaining major sections, to give you an idea about the content of the chapter.


Introspection

When we query a GraphQL server, we request root fields and sub-fields of those roots, sub-sub-fields of those sub-fields, and so forth, creating a tree of results.

The fields that we have focused on to date have been data: trips, plans, and things like that. However, GraphQL also has another set of fields representing metadata about what we are querying. This set of fields allows tools like GraphiQL to know the nature of what the server understands, so the tools can guide you, generate code for you (as with Apollo-Android), and so forth. Those fields can also be useful to you as part of your normal GraphQL requests, to give you some context around the response that you get.

These metadata fields form GraphQL’s introspection system, which we will focus on in this chapter.

Adding a Type To Your Response

In a previous chapter, we examined how to query on interfaces and unions. However, savvy developers will have noticed a bit of a hole in our query and its response: we do not get any information about what we are getting back.

For example, we had this GraphQL document:

query getAllTrips {
  allTrips {
    ...planFields
    plans {
      ...planFields
    }
  }
}

fragment planFields on Plan {
  id
  title
  startTime
  priority
  duration
  creationTime
}

That, in turn, generates this JSON response:

{
  "data": {
    "allTrips": [
      {
        "id": "2c494055-78bc-430c-9ab7-19817f3fc060",
        "title": "Vacation!",
        "startTime": "2017-12-20T13:14:00-05:00",
        "priority": "MEDIUM",
        "duration": 10080,
        "creationTime": "2017-02-19T15:21:58.547Z",
        "plans": [
          {
            "id": "319185bd-fab0-49e3-86ce-251d2aaa5d23",
            "title": "Flight to Chicago",
            "startTime": "2017-12-20T13:14:00-05:00",
            "priority": "HIGH",
            "duration": 150,
            "creationTime": "2017-02-19T15:21:58.547Z"
          },
          {
            "id": "319185bd-fab0-49e3-86ce-251d2aaa5d23",
            "title": "House of Munster",
            "startTime": "2017-12-20T15:00:00-05:00",
            "priority": "MEDIUM",
            "duration": 9900,
            "creationTime": "2017-02-19T15:21:58.547Z"
          }
        ]
      },
      {
        "id": "e323fed5-6805-4bcf-8cb6-8b7a5014a9d9",
        "title": "Business Trip",
        "startTime": "2018-01-14T11:45:00-05:00",
        "priority": "HIGH",
        "duration": 4320,
        "creationTime": "2017-02-19T15:21:58.547Z",
        "plans": [
          {
            "id": "d40eb2e7-3211-422e-858c-403cbe3fa680",
            "title": "Flight to Denver",
            "startTime": "2018-01-14T11:45:00-05:00",
            "priority": "HIGH",
            "duration": 257,
            "creationTime": "2017-02-19T15:21:58.547Z"
          },
          {
            "id": "e28a591b-cdc9-4328-9e79-9e4ed60ae7d2",
            "title": "Hotel Von",
            "startTime": "2018-01-14T15:00:00-05:00",
            "priority": "MEDIUM",
            "duration": 4140,
            "creationTime": "2017-02-19T15:21:58.547Z"
          }
        ]
      }
    ]
  }
}

We get back the fields that we ask for. However, there is nothing to tell us what the underlying type is behind the objects in the plans array. As humans, we can tell that the “Flight to Denver” Plan probably is a Flight, as that would be a rather strange name for some Lodging. However, we cannot readily teach software to make that distinction, nor should we need to.

Using inline fragments can help a little:

query getAllTrips {
  allTrips {
    ...planFields
    plans {
      ...planFields
      ... on Flight {
        airlineCode
        flightNumber
        departingAirport
        arrivingAirport
      }
      ... on Lodging {
        address
      }
    }
  }
}

fragment planFields on Plan {
  id
  title
  startTime
  priority
  duration
  creationTime
}

Now, we could say that if a Plan has an airlineCode, then it is a Flight, whereas if it has an address, then it is a Lodging. However, we are making an inference of the underlying types based on the fields that we get back. Wouldn’t it be nice if the JSON just said, plainly, what each of those objects are?

The good news is that we can get that, by adding a single additional field to planFields: __typename. This provides to us the type name of whatever object it is that we are obtaining fields from. So, by changing planFields to:

fragment planFields on Plan {
  __typename
  id
  title
  startTime
  priority
  duration
  creationTime
}

…we now get output like this:

{
  "data": {
    "allTrips": [
      {
        "__typename": "Trip",
        "id": "2c494055-78bc-430c-9ab7-19817f3fc060",
        "title": "Vacation!",
        "startTime": "2017-12-20T13:14:00-05:00",
        "priority": "MEDIUM",
        "duration": 10080,
        "creationTime": "2017-02-19T15:21:58.547Z",
        "plans": [
          {
            "__typename": "Flight",
            "id": "319185bd-fab0-49e3-86ce-251d2aaa5d23",
            "title": "Flight to Chicago",
            "startTime": "2017-12-20T13:14:00-05:00",
            "priority": "HIGH",
            "duration": 150,
            "creationTime": "2017-02-19T15:21:58.547Z"
          },
          {
            "__typename": "Lodging",
            "id": "319185bd-fab0-49e3-86ce-251d2aaa5d23",
            "title": "House of Munster",
            "startTime": "2017-12-20T15:00:00-05:00",
            "priority": "MEDIUM",
            "duration": 9900,
            "creationTime": "2017-02-19T15:21:58.547Z"
          }
        ]
      },
      {
        "__typename": "Trip",
        "id": "e323fed5-6805-4bcf-8cb6-8b7a5014a9d9",
        "title": "Business Trip",
        "startTime": "2018-01-14T11:45:00-05:00",
        "priority": "HIGH",
        "duration": 4320,
        "creationTime": "2017-02-19T15:21:58.547Z",
        "plans": [
          {
            "__typename": "Flight",
            "id": "d40eb2e7-3211-422e-858c-403cbe3fa680",
            "title": "Flight to Denver",
            "startTime": "2018-01-14T11:45:00-05:00",
            "priority": "HIGH",
            "duration": 257,
            "creationTime": "2017-02-19T15:21:58.547Z"
          },
          {
            "__typename": "Lodging",
            "id": "e28a591b-cdc9-4328-9e79-9e4ed60ae7d2",
            "title": "Hotel Von",
            "startTime": "2018-01-14T15:00:00-05:00",
            "priority": "MEDIUM",
            "duration": 4140,
            "creationTime": "2017-02-19T15:21:58.547Z"
          }
        ]
      }
    ]
  }
}

Because we added __typename to planFields, everywhere we are getting fields from a Plan — both the Trip and its plans — we get a __typename telling us how to interpret the data. So, now we have a positive indicator of what is a Flight and what is Lodging, instead of having to infer the type from the available fields.

Note that you can alias __typename, just as you can alias any other sort of field:

fragment planFields on Plan {
  type: __typename
  id
  title
  startTime
  priority
  duration
  creationTime
}

Now, we would get the type as a type field in the JSON, rather than as __typename.

Note that Apollo-Android does not allow you to retrieve the __typename field. Apollo-Android uses that internally for type resolution — this is how Apollo-Android can create Flight and Lodging objects correctly. However, as a result, if you try to add __typename yourself to a query, your build will fail. However, your result objects (e.g., AllTrip) have a __typename() method that will return the __typename value as retrieved by Apollo-Android.

Introspection Beyond the Type Name

The preview of this section was eaten by a grue.