This article is a part of free GraphQL Language course. To fully understand the content you need to be familiar with basic GraphQL concepts like SDL language, GraphQL document syntax or GraphQL object types and scalars. If you do not have this knowledge, you can take a look at our older articles on graphqlmastery.com and on our medium publication.

Introduction

In the GraphQL specification we are able to use two abstract types:

interfaces

unions

In this article we will go through the use cases for abstract types and how we can implement them in our GraphQL schema. Using abstract types can greatly improve your GraphQL schema design and simplify your queries and mutations.

What is an interface?

Let's first define what is interface in programming. There are various definitions for interfaces, but I found out that the most useful one for understanding GraphQL interfaces is as follows:

The interface is a structure that enforces certain properties on the object or class that implements the corresponding interface

Interfaces are usually needed when we are looking to access certain group of objects that has to comply to properties defined by interface. With interface we are basically abstracting group of types. By this abstraction we can reason about them as one entity.

Interfaces in GraphQL

Let's first define our model schema in SDL, so that we can start to apply our interface example. We would like to cover the types Planet, Constellation, Galaxy and Star. The types in SDL language can look for example like this:

type Galaxy { id : ID ! createdAt : DateTime ! updatedAt : DateTime name : String planets : [ Planet ] stars : [ Star ] } type Star { id : ID ! createdAt : DateTime ! updatedAt : DateTime name : String class : StellarClassEnum } type Constellation { id : ID ! createdAt : DateTime ! updatedAt : DateTime name : String galaxies : [ Galaxy ] } type Planet { id : ID ! createdAt : DateTime ! updatedAt : DateTime name : String description : String planetType : PlanetTypeEnum }

As we mentioned when applying interface we are basically adding additional "node" above our types, so that we are able to access them as one enity. Let's say that we would like to access all the types from our schema as one single entity called "Node". To visualize this let's take a look at the following graph

In GraphQL we need to define every interface with at least one field. For example, in Relay.js we use what is known as a Node interface. Node interface usually serves as an interface for every type, which has the id field. If you are not using Relay.js on your frontend, I would also recommend to use this pattern. In Relay.js the Node interface has just one field id and it looks like this

interface Node { id : ID ! }

Implementing our custom Node interface

In our schema we have modified the common structure of Node interface to have the timestamps createdAt and updatedAt. Now let ' s take a look at how the Node interface is applied in our schema using SDL language. We first need to define the interface itself.

interface Node { id : ID ! createdAt : DateTime ! updatedAt : DateTime }

When defining an interface we need to specify the fields that all children of the interface need to have. Once the interface is defined we need to choose what types should implement this particular interface. The Node interface in our case is implemented by all mentioned types in our model schema example. The modified SDL schema then looks as follows

type Galaxy implements Node { id : ID ! createdAt : DateTime ! updatedAt : DateTime name : String planets : [ Planet ] stars : [ Star ] } type Star implements Node { id : ID ! createdAt : DateTime ! updatedAt : DateTime name : String class : StellarClassEnum } type Constellation implements Node { id : ID ! createdAt : DateTime ! updatedAt : DateTime name : String galaxies : [ Galaxy ] } type Planet implements Node { id : ID ! createdAt : DateTime ! updatedAt : DateTime name : String description : String planetType : PlanetTypeEnum }

We can see that every type implemented by the interface also has the same fields in common id, createdAt and updatedAt. These fields need to be also explicitly written for the type. If that is not the case, the GraphQL schema will be invalid. We are not constrained by using just Node interface. We can abstract different groups of types in our schema, such as multiple types of users (usually known as an Actor interface). The greatest advantage to this is it allows us to access a group of types in one single logical entity. This leads to a much cleaner schema design, as well as reducing the complexity of frontend.

Reducing the schema complexity

Now let ' s look at an example in which we reduce the complexity of queries and mutations. In our schema we would like to fetch Constellation, Planet, Stars or Galaxy based on the id. One approach is to add the query with the argument id for each different type for example like this.

type Query { star ( id : ID ! ) : Star planet ( id : ID ! ) : Planet galaxy ( id : ID ! ) : Galaxy constellation ( id : ID ! ) : Constellation }

As you can see the approach is not scalable. The better way is to add just one query, node, which will return the Node interface. This query can be applied to retrieve any type which implements the Node interface. In most schemas you should implement the Node interface in every object type with id as the idis used as a global identifier. We can define the “node query” in SDL as follows:

type Query { node ( "" "The ID of the object" "" id : ID ! ) : Node }

We would like to fetch just one Planet with "node query". To be able to do that we first need to fetch the id for some Planetwhich we can use as an argument for the “node query”. We can obtainids of the Planets by executing for example this query.

query getPlanets { planets { nodes { id name } } }

After executing this query we receive the list of planets objects with the id and name for each planet.

{ "data": { "planets": { "nodes": [ { "id": "UGxhbmV0OjUwMTA1MTc5MzQ2Nzg1NDg2MQ==", "name": "Mars" }, { "id": "UGxhbmV0OjUwMTA1MTc5MzQ2Nzg1NDg2MA==", "name": "Earth" } ] } } }

We can then copy one of the ids and use the Node interface to fetch more detailed info about the planet with this query

query getPlanet ( $ id : ID ! ) { node ( id : $ id ) { ... on Planet { id name description createdAt updatedAt } } }

with the following variables

{ "id": "UGxhbmV0OjUwMTA1MTc5MzQ2Nzg1NDg2MQ==" }

Then you will receive the full detail of the planet

{ "data": { "node" { "id": "UGxhbmV0OjUwMTA1MTc5MzQ2Nzg1NDg2MQ==", "name": "Mars", "description": "Mars is the fourth planet from the Sun and the second-smallest planet in the Solar System after Mercury. In English, Mars carries a name of the Roman god of war, and is often referred to as the Red Planet.", "createdAt": "2018-10-14T15:20:56.668Z", "updatedAt": null } } }

This is the example that will be used a lot whenever you want to make a frontend for GraphQL-driven apps. We can have for example a table of planets, but we do not want to fetch the whole planet instantly as it would be too expensive performance-wise and we will not use all the planet fields. We can render the name of the planets and, once we need more details, we can fetch the planetdetails with the “node query”. In our GraphQL document above we have introduced a new feature in GraphQL called “fragments”. For more info on fragments you can take a look at our article. Fragments help us tell the GraphQL server which fields we would like to retrieve for each type implemented by the interface. Now let’s check out the second abstract type called union.