Join us from October 8-10 in New York City to learn the latest tips, trends, and news about GraphQL federation and API platform engineering.Join us for GraphQL Summit 2024 in NYC
Docs
Start for Free

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 object types are included in the union:

union 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 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 object types (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 object type a field 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 query 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 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:

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

Resolving a union

Before reading this section, learn about resolvers.

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 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:

const resolvers = {
SearchResult: {
__resolveType(obj, contextValue, 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,
});
const { url } = await startStandaloneServer(server);
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 GraphQL error.

Interface type

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

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

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

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

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:

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

Example

The following schema defines a Book interface, along with two object types 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 field's return type is an interface, clients can query that field for any subfields included in the interface:

query GetBooks {
books {
title
author
}
}

Clients can also query for subfields 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 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:

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

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:

const resolvers = {
Book: {
__resolveType(book, contextValue, 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
Rate articleRateEdit on GitHubEditForumsDiscord

© 2024 Apollo Graph Inc.

Privacy Policy

Company