'How can we introspect a GraphQL schema recursively?

If a customer does not know the schema and would like to introspect and understand a GraphQL API, it seems that GraphQL may not be able to support the recursive introspection. See the following example about my point

First of all, the following is my schema definition at high level:

// schema.js
...
...     
const AuthorType = new GraphQLObjectType({
  name: "Author",
  description: "This represent an author",
  fields: () => ({
    id: {type: new GraphQLNonNull(GraphQLString)},
    name: {type: new GraphQLNonNull(GraphQLString)},
    twitterHandle: {type: GraphQLString}
  })
});

const PostType = new GraphQLObjectType({
  name: "Post",
  description: "This represent a Post",
  fields: () => ({
    id: {type: new GraphQLNonNull(GraphQLString)},
    title: {type: new GraphQLNonNull(GraphQLString)},
    body: {type: GraphQLString},
    author: {
      type: AuthorType,
      resolve: function(post) {
        return _.find(Authors, a => a.id == post.author_id);
      }
    }
  })
});

// This is the Root Query
const BlogQueryRootType = new GraphQLObjectType({
  name: 'BlogAppSchema',
  description: "Blog Application Schema Query Root",
  fields: () => ({
    authors: {
      type: new GraphQLList(AuthorType),
      description: "List of all Authors",
      resolve: function() {
        return Authors
      }
    },
    posts: {
      type: new GraphQLList(PostType),
      description: "List of all Posts",
      resolve: function() {
        return Posts
      }
    }
  })
});

When someone queries the schema using the following query clause:

{
  __type(name: "BlogAppSchema") {
    name
    fields {
      name
      description
      type {
        name
      }
    }
  }
}

She gets the following result:

{
  "data": {
    "__type": {
      "name": "BlogAppSchema",
      "fields": [
        {
          "name": "authors",
          "description": "List of all Authors",
          "type": {
            "name": null
          }
        },
        {
          "name": "posts",
          "description": "List of all Posts",
          "type": {
            "name": null
          }
        }
      ]
    }
  }
}

Reading the source code, we know that authors are a list of AuthorType. But how can a user, not having access the source code, further introspect the field of 'authors' from the results she got above (the type field shows "null" here)? She does not seem to be able to know authors is a list of Author from the above result. Is there a way for her to further introspect?



Solution 1:[1]

The name field returns null because your AuthorType is wrapped with the GraphQLList wrapper. That means the field is returning information about the wrapper, not the underlying type. To get to the type, you have modify your request:

{
  __type(name: "BlogAppSchema") {
    name
    fields {
      name
      description
      type {
        name
        kind # this will show NON_NULL, LIST, SCALAR or OBJECT
        ofType { # if NON_NULL or LIST what is it a non-null or list *of*
          name
          kind
          # other fields, like "fields" which will be populated for an OBJECT
        }
      }
    }
  }
}

If you utilize multiple wrappers (i.e. [Author]! or [Author!]!), you will need to "go deeper" and request the nested ofType fields:

{
  __type(name: "BlogAppSchema") {
    name
    fields {
      name
      description
      type {
        name
        kind 
        ofType { 
          name
          kind
          ofType {
            name
            kind
            ofType {
              name
              kind
              ofType {
                name
                kind
              }
            }
          }
        }
      }
    }
  }
}

If the kind is OBJECT, its fields field will be populated appropriately. You can then request details for each field as in above. Of course, if there are any OBJECTs returned from those fields, you'll have to specify what information you want to get from those.

You can read more about introspection here.

Introspection can be very messy. If you need a way for the consumer of your GraphQL endpoint to explore the schema, GraphiQL is a much more user-friendly way of doing that. There's also ways of dynamically generating documentation (like this).

Solution 2:[2]

Is there any way of querying it recursively automatically, instead of like Daniel Rearden mentioned it with

{
  __type(name: "BlogAppSchema") {
    name
    fields {
      name
      description
      type {
        name
        kind 
        ofType { 
          name
          kind
          ofType {
            name
            kind
            ofType {
              name
              kind
              ofType {
                name
                kind
              }
            }
          }
        }
      }
    }
  }
}

Meaning that we don't have to specify all these deep nested ofType queries?

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Daniel Rearden
Solution 2 rexloxrix