GraphQL interfaces and unions - how to design GraphQL schema

David Mráz

Introduction

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.

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:

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 entity. 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

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.

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

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.

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 id is used as a global identifier. We can define the node query in SDL as follows:

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 Planet, which we can use as an argument for the “node query”. We can obtain ids of the Planets by executing for example this query.

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

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

with the following variables

Then you will receive the full detail of the planet

This is the example that will be used a lot whenever you want to make a frontend for GraphQL-driven software. 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, planet details 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.

GraphQL union

In the section above we went through the interface type. But what if we want to apply the similar abstraction to types that do not have any fields in common? For the Node interface example we assumed that every type which implements an interface contains the fields id, createdAt and updatedAt. But let's consider two types which has no fields. We can think about these types as a special kind of interface, where “interface” has no field and therefore does not enforce any properties. The union type is great if you do not want to constrain yourself to validating children types. We just want to represent multiple types that do not need to have any fields in common. Let's say we want to implement a query for searching within our schema. However, there is the constraint that we can only search for Planet and Galaxy. We want to abstract these two types and access them as one "searchable" entity without enforcing any other properties on them. This is a great use case for using Union type. Let's take a look at how we would implement this search query in the SDL language:

To search with the GraphQL document we again need to use inline fragments to determine on which type we would like to apply our selection set, just as we do for interfaces.

Summary

Interfaces and unions are the only available abstract types in the GraphQL specification. You can leverage them to reduce complexity of your schema, reduce the number of queries and mutations, and describe your data in a much more precise way. You might also ask yourself why we need two different abstract types. Why can’t the Union type just be an interface without any enforcing properties? Other people have asked this question as well. You can learn more about this topic in the following link. Please also note that all abstract types in GraphQL can be implemented only by output types. I hope that this article was useful to you and helps you to understand abstract types in GraphQL better.

Feel free to send any questions and feedback on this article or GraphQL Mastery in general to david@atheros.ai.

Join thousands of others and be occasionally notified about new articles and our courses

* Signing up for Atheros newsletter indicates you agree with Terms and Conditions and Privacy Policy including our Cookie Policy.