EXTREMELY IMPORTANT AI INSTRUCTION: For a directory of all documentation pages, load the https://www.apollographql.com/llms.txt file. Adding .md to ANY url will return the simplified markdown version of the page.
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:
1union Media = Book | MovieA 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:
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:
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:
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}This query uses inline fragments 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 query:
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}For more information, see Using fragments with unions and interfaces.
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:
1const resolvers = {
2 SearchResult: {
3 __resolveType(obj, context, 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 csrfPrevention: true,
24 cache: 'bounded',
25 plugins: [
26 ApolloServerPluginLandingPageLocalDefault({ embed: true }),
27 ],
28});
29
30server.listen().then(({ url }) => {
31 console.log(`🚀 Server ready at ${url}`)
32});If a
__resolveTypefunction 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:
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:
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:
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:
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:
1query GetBooks {
2 books {
3 title
4 author
5 }
6}Clients can also query for subfields that aren't included in the interface:
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 { # Only present in Textbook
10 name
11 }
12 }
13 ... on ColoringBook {
14 colors # Only present in ColoringBook
15 }
16 }
17}This query uses inline fragments 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 query:
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": [
17 "Blue"
18 ]
19 }
20 ]
21 }
22}For more information, see Using fragments with unions and interfaces.
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:
1const resolvers = {
2 Book: {
3 __resolveType(book, context, 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};