Table of Contents

Unions

union User | Admin

Unions allow you to group different objects into one single type. You can think of unions as a sort of composition. They work great with Spreads. Unlike interfaces, unions can’t have fields.

Creating a Union

You can define unions on a file or using the shortcut on the schema.

# app/graphql/unions/person.rb
module GraphQL
  class Person < GraphQL::Union
    # This is a union of User and Admin object types
    append User, Admin
  end
end

# OR

# app/graphql/app_schema.rb
union 'Person' do
  append User, Admin
end

# OR even
union 'Person', of_types: %w[User Admin]

The last one is preferable because, for now, the only thing you can do with unions besides appending the types is using directives. Plus, it follows the rule of not referencing types by their actual classes.

Read more about recommendations.

Description

Allows documenting unions. This value can be retrieved using introspection or during a to_gql output. Within the class, desc works as a syntax sugar for self.description = ''. It also supports descriptions from I18n.

# app/graphql/unions/person.rb
module GraphQL
  class Person < GraphQL::Union
    desc 'This is awesome!'
  end
end

Type Resolution

Unions have a special type_for(value, request) class-level method, which is responsible for finding an object for the given value.

The default behavior is to go over the list of members in reverse and find the first one that is a valid_member? of value.

If for some reason, that is not compatible with your application, you can override this method. For example, this relies on ActiveRecord single table inheritance column.

# app/graphql/unions/person.rb
def self.type_for(value, request)
  request.find_type(value.type)
end

Important It’s recommended to use request.find_type with either the symbol or string name because the resolution is cached during the request and complies with namespaces.

Such translation is possible because object names match type names when using sources. An even more accurate version would be
value.type.tr(':', '').

For gem Creators

Once you have created your unions in your gem, remember to add them into config.known_dependencies. It is not recommended to require such files in your gem.

Rails::GraphQL.config.known_dependencies[:union].update(
  my_gem_union: "#{__dir__}/unions/my_gem_union",
)

Using Unions

Once they are defined, you can set them as the type of output fields. Then, in your execution document, you can use spreads to properly capture unique information of the respective types.

Since unions do not contain a list of fields, you can never request fields directly from them besides __typename.

field(:recipient, 'Person')
{
  recipient {
    # Gets the name of the type
    __typename
    ... on User {
      # Email is a common attribute, but unions don't hold fields
      email
      # Slug is a field only available on User
      slug
    }
    ... on Admin {
      # Email is a common attribute, but unions don't hold fields
      email
      # Role is a field only available on Admin
      role
    }
  }
}

Unions is the best place for you to set up your belongs_to Polymorphic Associations.