Unions and interfaces

How to write add unions and interfaces to a schema

Unions and interfaces are great when you have fields that are in common between two types.

Union type

The Union type indicates that a field can return more than one object type, but doesn’t define specific fields itself. Unions are useful for returning disjoint data types from a single field. The type definitions appear as follows:

const { gql } = require('apollo-server');

const typeDefs = gql`
union Result = Book | Author

type Book {
  title: String
}

type Author {
  name: String
}

type Query {
  search: [Result]
}`;

Since a query requesting a union field, a query being made on a field which is union-typed must specify the object types containing the fields it wants. This ambiguity is solved by an extra __resolveType field in the resolver map. __resolveType defines the type of the result is out of the available options to GraphQL execution environment.

const resolvers = {
  Result: {
    __resolveType(obj, context, info){
      if(obj.name){
        return 'Author';
      }

      if(obj.title){
        return 'Book';
      }

      return null;
    },
  },
  Query: {
    search: () => { ... }
  },
};

const server = new ApolloServer({
  typeDefs,
  resolvers,
});

server.listen().then(({ url }) => {
  console.log(`🚀 Server ready at ${url}`)
});

A possible query for these result could appear as follows. This query demonstrates the need for the __resolveType, since it requests different data depending on the types,

{
  search(contains: "") {
    ... on Book {
      title
    }
    ... on Author {
      name
    }
  }
}

Interface type

An Interface type provides the ability to describe fields that are shared across different types. It is best used to show that all types implementing an interface always contain the interface’s fields. In other words, it is the semantic opposite of a union. For example, in this example Vehicle interface type is used by members Airplane and Car:

interface Vehicle {
  maxSpeed: Int
}

type Airplane implements Vehicle {
  maxSpeed: Int
  wingspan: Int
}

type Car implements Vehicle {
  maxSpeed: Int
  licensePlate: String
}

type Query {
  vehicle: Vehicle
}

Similarly to the Union, Interface requires an extra __resolveType field in the resolver map.

const resolvers = {
  Vehicle: {
    __resolveType(obj, context, info){
      if(obj.wingspan){
        return 'Airplane';
      }

      if(obj.licensePlate){
        return 'Car';
      }

      return null;
    },
  },
  Query: {
    vehicle: () => { ... }
  },
};

const server = new ApolloServer({
  typeDefs,
  resolvers,
});

server.listen().then(({ url }) => {
  console.log(`🚀 Server ready at ${url}`)
});

A possible query could appear as follows. Notice that maxSpeed is shared, so it can be included directly:

{
  vehicle {
    maxSpeed

    ... on Car {
			licensePlate
    }
    ... on Airplane {
      wingspan
    }
  }
}
Edit on GitHub
// search box