Docs
Launch GraphOS Studio
Apollo Server 3 is officially deprecated, with end-of-life scheduled for 22 October 2024. Learn more about upgrading to a supported Apollo Server version.

Unions and interfaces

Abstract schema types


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

Union type

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

union Media = Book | Movie

A can have a union (or a list of that union) as its return type. In this case, it can return any that's included in the union:

type Query {
allMedia: [Media] # This list can include both Book and Movie objects
}

All of a union's included types must be

(not , input types, etc.). Included types do not need to share any .

Example

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

union SearchResult = Book | Author
type Book {
title: String!
}
type Author {
name: String!
}
type Query {
search(contains: String): [SearchResult!]
}

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

Querying a union

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

Here's a valid for the schema above:

query GetSearchResults {
search(contains: "Shakespeare") {
# Querying for __typename is almost always recommended,
# but it's even more important when querying a field that
# might return one of multiple types.
__typename
... on Book {
title
}
... on Author {
name
}
}
}

This uses

to fetch a Result's title (if it's a Book) or its name (if it's an Author).

Here's a valid result for the above :

{
"data": {
"search": [
{
"__typename": "Book",
"title": "The Complete Works of William Shakespeare"
},
{
"__typename": "Author",
"name": "William Shakespeare"
}
]
}
}

For more information, see

.

Resolving a union

Before reading this section,

.

To fully resolve a union, 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 map.

The __resolveType function is responsible for determining an object's corresponding 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 that are unique to a particular type in the union
  • Using instanceof, if the JavaScript object's type is related to its GraphQL

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

const resolvers = {
SearchResult: {
__resolveType(obj, context, info){
// Only Author has a name field
if(obj.name){
return 'Author';
}
// Only Book has a title field
if(obj.title){
return 'Book';
}
return null; // GraphQLError is thrown
},
},
Query: {
search: () => { ... }
},
};
const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
cache: 'bounded',
plugins: [
ApolloServerPluginLandingPageLocalDefault({ embed: true }),
],
});
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`)
});

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

Interface type

An interface specifies a set of that multiple can include:

interface Book {
title: String!
author: Author!
}

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

type Textbook implements Book {
title: String! # Must be present
author: Author! # Must be present
courses: [Course!]!
}

A can have an interface (or a list of that interface) as its return type. In this case, it can return any that implements that interface:

type Query {
books: [Book!] # Can include Textbook objects
}

Example

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

interface Book {
title: String!
author: Author!
}
type Textbook implements Book {
title: String!
author: Author!
courses: [Course!]!
}
type ColoringBook implements Book {
title: String!
author: Author!
colors: [String!]!
}
type Query {
books: [Book!]!
}

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

Querying an interface

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

query GetBooks {
books {
title
author
}
}

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

query GetBooks {
books {
# Querying for __typename is almost always recommended,
# but it's even more important when querying a field that
# might return one of multiple types.
__typename
title
... on Textbook {
courses { # Only present in Textbook
name
}
}
... on ColoringBook {
colors # Only present in ColoringBook
}
}
}

This uses

to fetch a Book's courses (if it's a Textbook) or its colors (if it's a ColoringBook).

Here's a valid result for the above :

{
"data": {
"books": [
{
"__typename": "Textbook",
"title": "Wheelock's Latin",
"courses": [
{
"name": "Latin I"
}
]
},
{
"__typename": "ColoringBook",
"title": "Oops All Water",
"colors": [
"Blue"
]
}
]
}
}

For more information, see

.

Resolving an interface

Before reading this section,

.

, requires interfaces to define a __resolveType function to determine which implementing is being returned.

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

const resolvers = {
Book: {
__resolveType(book, context, info){
// Only Textbook has a courses field
if(book.courses){
return 'Textbook';
}
// Only ColoringBook has a colors field
if(book.colors){
return 'ColoringBook';
}
return null; // GraphQLError is thrown
},
},
Query: {
books: () => { ... }
},
};
Previous
Schema basics
Next
Custom scalars
Edit on GitHubEditForumsDiscord

© 2024 Apollo Graph Inc.

Privacy Policy

Company