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 (or a list of that 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 Book and Movie objects
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 SearchResult union type that can return either a Book or an Author:

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

The SearchResult 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    # Querying for __typename is almost always recommended,
4    # but it's even more important when querying a field that
5    # might return one of multiple types.
6    __typename
7    ... on Book {
8      title
9    }
10    ... on Author {
11      name
12    }
13  }
14}

What is the __typename field?

This query uses inline fragments to fetch a Result's title (if it's a Book) or its name (if it's an Author). The web client can be informed about this polymorphic relationship by passing the possibleTypes option.

Here's a valid result for the above query:

JSON
1{
2  "data": {
3    "search": [
4      {
5        "__typename": "Book",
6        "title": "The Complete Works of William Shakespeare"
7      },
8      {
9        "__typename": "Author",
10        "name": "William Shakespeare"
11      }
12    ]
13  }
14}

Resolving a union

Before reading this section, learn about resolvers.

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 is responsible for determining an object's corresponding GraphQL type and returning the name of that type as a string. It can use any logic to do so, such as:

  • Checking for the presence or absence of fields that are unique to a particular type in the union

  • Using instanceof, if the JavaScript object's type is related to its GraphQL object type

Here's a basic __resolveType function for the SearchResult union defined above:

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

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 (or a list of that interface) as its return type. In this case, it can return any object type that implements that interface:

GraphQL
1type Query {
2  books: [Book!]! # Can include Textbook objects
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: [String!]!
16}
17
18type Query {
19  books: [Book!]!
20}

In this schema, Query.books 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  books {
3    title
4    author
5  }
6}

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

GraphQL
1query GetBooks {
2  books {
3    # Querying for __typename is almost always recommended,
4    # but it's even more important when querying a field that
5    # might return one of multiple types.
6    __typename
7    title
8    ... on Textbook {
9      courses {
10        # Only present in Textbook
11        name
12      }
13    }
14    ... on ColoringBook {
15      colors # Only present in ColoringBook
16    }
17  }
18}

What is the __typename field?

This query uses inline fragments to fetch a Book's courses (if it's a Textbook) or its colors (if it's a ColoringBook). The web client can be informed about this polymorphic relationship by passing the possibleTypes option.

Here's a valid result for the above query:

JSON
1{
2  "data": {
3    "books": [
4      {
5        "__typename": "Textbook",
6        "title": "Wheelock's Latin",
7        "courses": [
8          {
9            "name": "Latin I"
10          }
11        ]
12      },
13      {
14        "__typename": "ColoringBook",
15        "title": "Oops All Water",
16        "colors": ["Blue"]
17      }
18    ]
19  }
20}

Resolving an interface

Before reading this section, learn about resolvers.

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:

TypeScript
1const resolvers = {
2  Book: {
3    __resolveType(book, contextValue, info){
4      // Only Textbook has a courses field
5      if(book.courses){
6        return 'Textbook';
7      }
8      // Only ColoringBook has a colors field
9      if(book.colors){
10        return 'ColoringBook';
11      }
12      return null; // GraphQLError is thrown
13    },
14  },
15  Query: {
16    books: () => { ... }
17  },
18};
Feedback

Edit on GitHub

Forums