Introduction
If you are searching for the whole introspection query for fetching the whole GraphQL schema, you can follow up this link. We expect that reader has at least some familiarity with basic GraphQL concepts. This article is a part of our free GraphQL Language course.
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 GraphQL Playground tool. With the GraphQL Playground tool we are able to query the whole type system of the schema. This helps us to document schema live or use GraphQL Playground for our development. It also allows us to use introspection queries to gain information 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 GraphQL Playground 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: __TypesubscriptionType: __Typedirectives: [__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 {namedescription}subscriptionType {namedescription}types {namedescription}queryType {namedescription}mutationType {namedescription}queryType {namedescription}}}
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 GraphQL Playground itself. These fragments look like this:
fragment FullType on __Type {kindnamedescriptionfields(includeDeprecated: true) {namedescriptionargs {...InputValue}type {...TypeRef}isDeprecateddeprecationReason}inputFields {...InputValue}interfaces {...TypeRef}enumValues(includeDeprecated: true) {namedescriptionisDeprecateddeprecationReason}possibleTypes {...TypeRef}}fragment InputValue on __InputValue {namedescriptiontype {...TypeRef}defaultValue}fragment TypeRef on __Type {kindnameofType {kindnameofType {kindnameofType {kindnameofType {kindnameofType {kindnameofType {kindnameofType {kindname}}}}}}}}
We can see that GraphQL Playground 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 GraphQL Playground introspection in use:
query IntrospectionQuery {__schema {queryType {name}mutationType {name}types {...FullType}directives {namedescriptionlocationsargs {...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 {idnamecreatedAtupdatedAt__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
- Getting all fields for a certain type
- Enumerate all possible interfaces
The whole introspection query, which is used by GraphQL Playground itself can be copied from this gist.