Introduction to Unions

In GraphQL, unions are a powerful feature that allows you to define a type that can represent one of several different object types. This is particularly useful when you have a field that can return multiple types of objects, and you want to handle them in a type-safe manner.

Key Concepts

  • Union Type: A union type is a special kind of type that can represent one of several object types.
  • Type Safety: Unions help maintain type safety by ensuring that the field returns one of the specified types.
  • Type Resolution: When querying a union type, the GraphQL server must resolve which type is being returned.

Defining Unions

To define a union in GraphQL, you use the union keyword followed by the name of the union and the types it can represent.

Example

Let's consider a scenario where we have a SearchResult union that can be either a Book or an Author.

# Define the Book type
type Book {
  title: String!
  author: Author!
}

# Define the Author type
type Author {
  name: String!
  books: [Book!]!
}

# Define the SearchResult union
union SearchResult = Book | Author

# Define a query that returns a SearchResult
type Query {
  search(query: String!): SearchResult
}

In this example:

  • The SearchResult union can be either a Book or an Author.
  • The search query returns a SearchResult, which means it can return either a Book or an Author.

Querying Unions

When querying a union type, you need to use inline fragments to specify the fields you want to retrieve for each possible type.

Example

query {
  search(query: "GraphQL") {
    ... on Book {
      title
      author {
        name
      }
    }
    ... on Author {
      name
      books {
        title
      }
    }
  }
}

In this query:

  • We use inline fragments (... on Book and ... on Author) to specify the fields we want to retrieve for each type.
  • This ensures that we only request fields that are valid for the returned type.

Implementing Resolvers for Unions

When implementing resolvers for union types, you need to provide a type resolver function that determines which type is being returned.

Example

const resolvers = {
  Query: {
    search: (parent, args, context, info) => {
      // Example search logic
      if (args.query === "GraphQL") {
        return { title: "Learning GraphQL", author: { name: "Eve Porcello" } };
      } else {
        return { name: "Eve Porcello", books: [{ title: "Learning GraphQL" }] };
      }
    },
  },
  SearchResult: {
    __resolveType(obj, context, info) {
      if (obj.title) {
        return 'Book';
      }
      if (obj.name) {
        return 'Author';
      }
      return null;
    },
  },
};

In this example:

  • The search resolver returns either a Book or an Author based on the query.
  • The __resolveType function determines the type of the returned object by checking its properties.

Practical Exercise

Exercise

  1. Define a union type Media that can be either a Movie or a TVShow.
  2. Create a query mediaSearch that returns a Media.
  3. Implement the resolver for mediaSearch and the type resolver for Media.

Solution

# Define the Movie type
type Movie {
  title: String!
  director: String!
}

# Define the TVShow type
type TVShow {
  title: String!
  seasons: Int!
}

# Define the Media union
union Media = Movie | TVShow

# Define a query that returns a Media
type Query {
  mediaSearch(query: String!): Media
}
const resolvers = {
  Query: {
    mediaSearch: (parent, args, context, info) => {
      // Example search logic
      if (args.query === "Inception") {
        return { title: "Inception", director: "Christopher Nolan" };
      } else {
        return { title: "Breaking Bad", seasons: 5 };
      }
    },
  },
  Media: {
    __resolveType(obj, context, info) {
      if (obj.director) {
        return 'Movie';
      }
      if (obj.seasons) {
        return 'TVShow';
      }
      return null;
    },
  },
};

Common Mistakes and Tips

  • Missing Type Resolver: Always provide a type resolver function for unions to ensure the server can determine the correct type.
  • Invalid Fields: When querying unions, ensure that you only request fields that are valid for the returned type.
  • Clear Type Distinction: Make sure the types in a union are distinct enough to be easily resolved.

Conclusion

Unions in GraphQL provide a flexible way to handle fields that can return multiple types. By defining unions and implementing type resolvers, you can maintain type safety and ensure your API is robust and easy to use. In the next section, we will explore another advanced schema design feature: Enums.

© Copyright 2024. All rights reserved