August 1st 2018 (3 months ago)

GraphQL introspection and introspection queries

Author profile picture
David Mráz (@david_mraz1)

Introduction

If you are searching for the whole introspection query for fetching the whole GraphQL schema, you can follow up this link. This article is part of module on GraphQL Introspection in upcoming free GraphQL Language course. We expect that reader has at least some familiarity with basic GraphQL concepts. Otherwise you can go through some of our older articles on medium publication or check out other available resources.

In previous chapters we’ve discussed how the GraphQL language is strongly typed. This is a great advantage over REST APIs. The GraphQL type system not only reduces errors, but also helps us to design the system with much better architecture. In addition, the type system allows us to develop various tools and mechanisms that can increase efficiency of development. One of the best examples of this is the GraphiQL tool from Facebook, which we use throughout the course. With the GraphiQL tool we are able to query the whole type system of the schema. This helps us to document schema live or use GraphiQL for our development. It also allows us to use introspection queries to gain infromation about the type system and reduce the frontend complexity. In this article we will go deeper into how introspection queries work, what kind of introspection queries we can use; and how we can apply them.

Introspection queries

All introspection queries in GraphQL are prefixed with two underscores, e.g. __schema. Therefore in the GraphQL schema we cannot define the new fields with this prefix in the name. We need to be sure to avoid naming collisions. Therefore all names with this prefix are not valid in common type fields. According to the specification, we have the following options for introspection queries: __schema, __type and __typename. Using SDL (schema definition language) we can define these queries as follows:

__schema: __Schema!__type(name: String!): __Type__typename: String!

In every GraphQL server we should be able to execute these introspection queries within the Query operation type.

__schema - introspection query for fetching GraphQL schema

The most important query, which is also used as the primary source for GraphiQL itself is the query that enables us to fetch the whole schema. The name of this query is __schema and its SDL definition is

type __Schema {
  types: [__Type!]!
  queryType: __Type!
  mutationType: __Type
  subscriptionType: __Type
  directives: [__Directive!]!
}

With this query we can gain information about the directives, available types, and available operation types. The basic introspection query for fetching the type system of the schema might look like this:

{
  __schema {
    directives {
      name
      description
    }
    subscriptionType {
      name
      description
    }
    types {
      name
      description
    }
    queryType {
      name
      description
    }
    mutationType {
      name
      description
    }
    queryType {
      name
      description
    }
  }
}

However, a lot of fields in the selection sets for queryType, mutationType, subscriptionType and types are the same, so we can define the fragment to have reusable pieces of logic and make the query much cleaner and also enhance selection set to fetch better detail of the type. We can even take fragments used in GraphiQL itself. These fragments look like this:

fragment FullType on __Type {
  kind
  name
  description
  fields(includeDeprecated: true) {
    name
    description
    args {
      ...InputValue
    }
    type {
      ...TypeRef
    }
    isDeprecated
    deprecationReason
  }
  inputFields {
    ...InputValue
  }
  interfaces {
    ...TypeRef
  }
  enumValues(includeDeprecated: true) {
    name
    description
    isDeprecated
    deprecationReason
  }
  possibleTypes {
    ...TypeRef
  }
}
fragment InputValue on __InputValue {
  name
  description
  type {
    ...TypeRef
  }
  defaultValue
}
fragment TypeRef on __Type {
  kind
  name
  ofType {
    kind
    name
    ofType {
      kind
      name
      ofType {
        kind
        name
        ofType {
          kind
          name
          ofType {
            kind
            name
            ofType {
              kind
              name
              ofType {
                kind
                name
              }
            }
          }
        }
      }
    }
  }
}

We can see that GraphiQL is using the fragment TypeRef, which recursively queries the GraphQL schema. This may have some limitations, but the fragment should have enough query depth to cover almost all schemas. We can prevent malicious queries by disabling extra query depth on the GraphQL server. Let’s execute this query to see the whole GraphiQL introspection in use:

query IntrospectionQuery {
  __schema {
    queryType {
      name
    }
    mutationType {
      name
    }
    types {
      ...FullType
    }
    directives {
      name
      description
      locations
      args {
        ...InputValue
      }
    }
  }
}

As a result, we should obtain the GraphQL schema in JSON format. In our case it is as follows

{
  "data": {
    "__type": {
      "kind": "OBJECT",
      "name": "Planet",
      "description": null,
      "fields": [
        {
          "name": "id",
          "description": null,
          "args": [],
          "type": {
            "kind": "NON_NULL",
            "name": null,
            "ofType": {
              "kind": "SCALAR",
              "name": "ID",
              "ofType": null
            }
          },
          "isDeprecated": false,
          "deprecationReason": null
        },
        {
          "name": "createdAt",
          "description": null,
          "args": [],
          "type": {
            "kind": "NON_NULL",
            "name": null,
            "ofType": {
              "kind": "SCALAR",
              "name": "DateTime",
              "ofType": null
            }
          },
          "isDeprecated": false,
          "deprecationReason": null
        },
        {
          "name": "updatedAt",
          "description": null,
          "args": [],
          "type": {
            "kind": "SCALAR",
            "name": "DateTime",
            "ofType": null
          },
          "isDeprecated": false,
          "deprecationReason": null
        },
        {
          "name": "name",
          "description": null,
          "args": [],
          "type": {
            "kind": "SCALAR",
            "name": "String",
            "ofType": null
          },
          "isDeprecated": false,
          "deprecationReason": null
        },
        {
          "name": "description",
          "description": null,
          "args": [],
          "type": {
            "kind": "SCALAR",
            "name": "String",
            "ofType": null
          },
          "isDeprecated": false,
          "deprecationReason": null
        },
        {
          "name": "planetType",
          "description": null,
          "args": [],
          "type": {
            "kind": "ENUM",
            "name": "PlanetTypeEnum",
            "ofType": null
          },
          "isDeprecated": false,
          "deprecationReason": null
        }
      ],
      "inputFields": null,
      "interfaces": [
        {
          "kind": "INTERFACE",
          "name": "Node",
          "ofType": null
        }
      ],
      "enumValues": null,
      "possibleTypes": null
    }
  }
}

We can see with this query that we are able to fetch information about all these different features of the schema

  • Queries
  • Mutations
  • Subscriptions
  • Types
  • Directives

__type

When using the introspection query __type we are able to query for the exact type we are interested in. To do so, we are required to specify the argument name of the type. To get full information about a certain type we can use the type fields from above and try to call an introspection query on the type called Planet in our schema. We can reuse the FullType fragment again to save some code, so the query would look like this:

query introspectionPlanetType {
  __type(name: "Planet") {
    ...FullType
  }
}

__typename

This is the third and last introspection query available. The difference is that this query is available through all types when querying. This shows us what type we are querying and is important for caching clients like Apollo or Relay to construct the cache. Let’s take a look at what happens when we execute it with our Planets query.

{
  planets {
    nodes {
      id
      name
      createdAt
      updatedAt
      __typename
    }
  }
}
{
  "data": {
    "planets": {
      "nodes": [
        {
          "id": "470206925720530945",
          "name": "Mars",
          "createdAt": "2018-07-21T12:34:27.314Z",
          "updatedAt": null,
          "__typename": "Planet"
        },
        {
          "id": "470206925720530944",
          "name": "Earth",
          "createdAt": "2018-07-21T12:34:27.314Z",
          "updatedAt": null,
          "__typename": "Planet"
        }
      ]
    }
  }
}

We can see that __typename is equal to the name of the type, which in our case is Planet. This is also useful to determine unique ids in caching clients. For example, in the Apollo client we use the

id + __typename

by default for normalization (if both are available) as unique ids to update the cache automatically.

Applications

As we have already discussed, introspection queries are mainly used for live documentation of the schema and to create rich IDEs for executing various requests. The most well known IDEs are built on top of GraphiQL. For me personally, I prefer to use GraphQL Playground on big projects, as it has some great additional features such as header definitions for GraphQL requests. Introspection queries can also be used in many different use cases to reduce frontend complexity and to avoid duplicating logic:

  • Fetching enum values for drop downs
  • Getting all fields for a certain type (great for displaying columns in the table)
  • Enumerate all possible interfaces (applicable when making applications that require a lot of abstractions)

More tips on the applications of introspection queries will be available in the Lead Backend Engineer course on GraphQL mastery. To get access to browser exercises on introspection and get more tips on GraphQL, be sure to subscribe to the interest list below for our free GraphQL Language course (coming soon).

The whole introspection query, which is used by GraphiQL itself can be copied from this gist.

Feel free to send any questions and feedback on this article or GraphQL Mastery in general to david@atheros.ai. You can also check out our upcoming free GraphQL language course or subscribe for new articles on GraphQL Mastery with the form below. If you prefer you can also checkout republished medium version of the article.

Join our newsletter to get notified about new articles and get instant access to our upcoming free GraphQL Course