Intermediate Interfaces & Generic Utility Types in GraphQL
Tags: graphql
Published: 2020-04-02 16:20:05

---
title: "Intermediate Interfaces & Generic Utility Types in GraphQL"
published: true
description: GraphQL now supports new type relationships by allowing interfaces to implement other interfaces.
tags: graphql
---

[Version 15](https://github.com/graphql/graphql-js/releases/tag/v15.0.0) of graphql-js (which was just released today!) introduces a feature that enables the representation of **generic utility types** in its type system. It doesn't introduce any new syntax or concepts, but expands the type system's ability to express the relationships between interfaces: an interface can now declare that it implements other interfaces.

To be clear, this is _not_ about creating deep hierarchies; instead, it is a minimal change that enables the expression of a very specific type of relationship that was previously inexpressible.

## Interfaces

Let's step back for a minute and talk about interfaces in general. The [interface](https://graphql.org/learn/schema/#interfaces) is a particularly useful construct that gives us the ability to define a common shape to be implemented by other types. For example, if we are building an app to help manage a zoo's breeding programs, we might define an `Animal` interface that contains the basic fields required on animals across all breeding programs:

```graphql
interface Animal {
  id: ID!
  name: String!
  mother: Animal
  father: Animal
}
```

Now that we have an `Animal` interface, we can _implement_ it in types that are specific to each breading program:

```graphql
type AfricanElephant implements Animal {
  id: ID!
  name: String!
  mother: AfricanElephant
  father: AfricanElephant

  # Fields specific to the african elephant...
  leftTuskLength: Float
  rightTuskLength: Float
}

type Alala implements Animal {
  id: ID!
  name: String!
  mother: Alala
  father: Alala

  # Fields specific to the ‘alalā...
  beakLength: Float
}
```

Notice that we needed to redefine each field specified in the interface. Each redefined field must either be exactly the same type as the interface's field, or a _refinement_ of the type defined by the interface. In our example, `Animal.father` is defined to be of the type `Animal`, but `AfricanElephant.father` is defined as `AfricanElephant`. This is perfectly valid because `AfricanElephant` implements the `Animal` interface.

## Lists

Now let's extend our schema to introduce a generic utility type: the GraphQL list.

```graphql
extend interface Animal {
  children: [Animal]
}

extend type AfricanElephant {
  children: [AfricanElephant]
}

extend type Alala {
  children: [Alala]
}
```

This works perfectly! The [GraphQL spec defines a list](https://graphql.github.io/graphql-spec/draft/#sec-Type-System.List) and makes it clear that the same rules propagate through lists: `[AfricanElephant]` is a valid refinement of `[Animal]`.

## Connections

However, GraphQL has never had a mechanism for extending these rules to _custom_ generic utility types. Instead of using lists directly, let's rework this example to follow the [Relay Connection Spec](https://facebook.github.io/relay/graphql/connections.htm). Let's try writing our server's relay connection implementation in GraphQL:

```graphql
type PageInfo {
  hasPreviousPage: Boolean!
  hasNextPage: Boolean!
  startCursor: String!
  endCursor: String!
}

interface Node {
  id: ID!
}

interface Edge {
  cursor: String!
  node: Node
}

interface Connection {
  pageInfo: PageInfo!
  edges: [Edge]
}
```

This is slightly more strict than the spec, and perfectly describes the guarantees our app can make. If you're unfamiliar with connections in GraphQL, Apollo has [an excellent writeup](https://blog.apollographql.com/explaining-graphql-connections-c48b7c3d6976) that is a perfect primer.

Now let's start creating our edge and connection types:

```graphql
# Animal
type AnimalEdge implements Edge {
  cursor: String!
  node: Animal
}

type AnimalConnection implements Connection {
  pageInfo: PageInfo!
  edges: [AnimalEdge]
}

extend interface Animal {
  children: AnimalConnection
}

# African Elephant
type AfricanElephantEdge implements Edge {
  cursor: String!
  node: AfricanElephant
}

type AfricanElephantConnection implements Connection {
  pageInfo: PageInfo!
  edges: [AfricanElephantEdge]
}

extend type AfricanElephant implements Node {
  children: AfricanElephantConnection
}

# Alala
type AlalaEdge implements Edge {
  cursor: String!
  node: Alala
}

type AlalaConnection implements Connection {
  pageInfo: PageInfo!
  edges: [AlalaEdge]
}

extend type Alala implements Node {
  children: AlalaConnection
}
```

We've created all our `Edge` and `Connection` types, added our `children` fields, and extended `AfricanElephant` and `Alala` to implement the `Node` interface... but we've ended up with an invalid schema!

While this was representable using "list" directly, we've lost the ability to convey certain relationships. In particular:

- `AnimalEdge.node` is invalid because we have no way to tell GraphQL that every `Animal` will _also_ implement `Node`.
- Both `AfricanElephant.children` and `Alala.children` are invalid, because we have no way to tell GraphQL that `AfticanElephantConnection` and `AlalaConnection` are implementations of `AnimalConnection`.

## Intermediate Interfaces

And this is what the new changes address. Let's rewrite this using our new ability to tell GraphQL that an interface implements another interface:

```graphql
# Animal
interface AnimalEdge implements Edge {
  cursor: String!
  node: Animal
}

interface AnimalConnection implements Connection {
  pageInfo: PageInfo!
  edges: [AnimalEdge]
}

extend interface Animal implements Node {
  children: AnimalConnection
}

# African Elephant
type AfricanElephantEdge implements AnimalEdge & Edge {
  cursor: String!
  node: AfricanElephant
}

type AfricanElephantConnection implements AnimalConnection & Connection {
  pageInfo: PageInfo!
  edges: [AfricanElephantEdge]
}

extend type AfricanElephant implements Node {
  children: AfricanElephantConnection
}

# Alala
type AlalaEdge implements AnimalEdge & Edge {
  cursor: String!
  node: Alala
}

type AlalaConnection implements AnimalConnection & Connection {
  pageInfo: PageInfo!
  edges: [AlalaEdge]
}

extend type Alala implements Node {
  children: AlalaConnection
}
```

It worked: our schema can now use our custom pagination types! We're now using GraphQL to describe the shape of our breeding program data, while _also_ describing the shape of our generic utility types.

By describing these utility types in GraphQL, our tooling can introspect our schema and know _exactly_ what our types do, without relying on descriptions or naming conventions. Our clients also gain the ability to create fragments directly on `Edge` and `Connection` interfaces!

## The Details

Finally, let's examine the rules for interfaces that implement other interfaces:

1. All fields of the implemented interface must be redefined with identical or refined types on the implementing type or interface. For example, because `Edge` defines the `cursor` field, `AnimalEdge` and `AfricanElephantEdge` must also define the cursor field.
2. All interface implementations declared on the implemented interface must be redeclared on the implementing type or interface. For example, because `Animal` declares that it implements `Node`, `AfricanElephant` must also declare that it implements `Node` in addition to implementing `Animal`.

That's it! That's all a schema author needs to know to use this new ability .

Because these rules flatten the schema at the time of authorship, nearly all tooling should continue to work when a schema begins using this pattern. Tooling only needs to change to allow spreads on intermediate interfaces in queries. For implementors of these tools, the introspection schema has a small update: the `interfaces` field of `__Type` will now return a list of interfaces when `kind` is `INTERFACE`, just like it does when `kind` is `OBJECT`.

## Links

- [The RFC to the GraphQL Spec](https://github.com/graphql/graphql-spec/pull/373)
- [The implementation in graphql-js](https://github.com/graphql/graphql-js/pull/2084)