Apollo Server 2 is officially end-of-life as of 22 October 2023.

Learn more about upgrading.

Unions and interfaces

Abstract schema types


Unions and interfaces are abstract GraphQL types that enable a schema field to return one of multiple object types.

Union type

When you define a union type, you declare which object types are included in the union:

GraphQL
1union Media = Book | Movie

A field can have a union as its return type. In this case, it can return any object type that's included in the union:

GraphQL
1type Query {
2  allMedia: [Media] # This list can include both Books and Movies
3}

All of a union's included types must be object types (not scalars, input types, etc.). Included types do not need to share any fields.

Example

The following schema defines a Result union type that can return either a Book or an Author:

GraphQL
1union Result = Book | Author
2
3type Book {
4  title: String
5}
6
7type Author {
8  name: String
9}
10
11type Query {
12  search(contains: String): [Result]
13}

The Result union enables Query.search to return a list that includes both Books and Authors.

Querying a union

GraphQL clients don't know which object type a field will return if the field's return type is a union. To account for this, a query can include the subfields of multiple possible types.

Here's a valid query for the schema above:

GraphQL
1query GetSearchResults {
2  search(contains: "Shakespeare") {
3    ... on Book {
4      title
5    }
6    ... on Author {
7      name
8    }
9  }
10}

This query uses inline fragments to fetch a Result's title (if it's a Book) or its name (if it's an Author).

For more information, see Using fragments with unions and interfaces.

Resolving a union

To fully resolve a union, Apollo Server needs to specify which of the union's types is being returned. To achieve this, you define a __resolveType function for the union in your resolver map.

The __resolveType function uses a returned object's fields to determine its type. It then returns the name of that type as a string.

Here's an example __resolveType function for the Result union defined above:

JavaScript
1const resolvers = {
2  Result: {
3    __resolveType(obj, context, info){
4      if(obj.name){
5        return 'Author';
6      }
7      if(obj.title){
8        return 'Book';
9      }
10      return null; // GraphQLError is thrown
11    },
12  },
13  Query: {
14    search: () => { ... }
15  },
16};
17
18const server = new ApolloServer({
19  typeDefs,
20  resolvers,
21});
22
23server.listen().then(({ url }) => {
24  console.log(`🚀 Server ready at ${url}`)
25});

If a __resolveType function returns any value that isn't the name of a valid type, the associated operation produces a GraphQL error.

Interface type

An interface specifies a set of fields that multiple object types can include:

GraphQL
1interface Book {
2  title: String
3  author: Author
4}

If an object type implements an interface, it must include all of that interface's fields:

GraphQL
1type Textbook implements Book {
2  title: String # Must be present
3  author: Author # Must be present
4  courses: [Course]
5}

A field can have an interface as its return type. In this case, it can return any object type that implements that interface:

GraphQL
1type Query {
2  schoolBooks: [Book] # Can include Textbooks
3}

Example

The following schema defines a Book interface, along with two object types that implement it:

GraphQL
1interface Book {
2  title: String
3  author: Author
4}
5
6type Textbook implements Book {
7  title: String
8  author: Author
9  courses: [Course]
10}
11
12type ColoringBook implements Book {
13  title: String
14  author: Author
15  colors: [Color]
16}
17
18type Query {
19  schoolBooks: [Book]
20}

In this schema, Query.schoolBooks returns a list that can include both Textbooks and ColoringBooks.

Querying an interface

If a field's return type is an interface, clients can query that field for any subfields included in the interface:

GraphQL
1query GetBooks {
2  schoolBooks {
3    title
4    author
5  }
6}

Clients can also query for subfields that aren't included in the interface:

GraphQL
1query GetBooks {
2  schoolBooks {
3    title # Always present (part of Book interface)
4    ... on Textbook {
5      courses { # Only present in Textbook
6        name
7      }
8    }
9    ... on ColoringBook {
10      colors { # Only present in ColoringBook
11        name
12      }
13    }
14  }
15}

This query uses inline fragments to fetch a Book's courses (if it's a Textbook) or its colors (if it's a ColoringBook).

For more information, see Using fragments with unions and interfaces.

Resolving an interface

As with union types, Apollo Server requires interfaces to define a __resolveType function to determine which implementing object type is being returned.

Here's an example __resolveType function for the Book interface defined above:

JavaScript
1const resolvers = {
2  Book: {
3    __resolveType(book, context, info){
4      if(book.courses){
5        return 'Textbook';
6      }
7      if(book.colors){
8        return 'ColoringBook';
9      }
10      return null; // GraphQLError is thrown
11    },
12  },
13  Query: {
14    schoolBooks: () => { ... }
15  },
16};
Feedback

Edit on GitHub

Forums