Group multiple object types into a single type for polymorphic field returns
Unions allow you to group different object types into a single type. They’re perfect for fields that can return one of several possible object types, like polymorphic associations or search results.
Same base class: All members must share the same base class
Object types only: Members must be GraphQL::Object types
# ✓ Validunion 'SearchResult' do append :post, :comment # Both are objectsend# ✗ Invalid - different base classesunion 'Invalid' do append :user, :post_input # Object + Input typeend# ✗ Invalid - no membersunion 'Empty' do # Raises: "A union must contain at least one member"end
Override for specific logic, like polymorphic associations:
union 'Commentable' do append :post, :video def self.type_for(value, request) # Use Rails polymorphic type column request.find_type(value.commentable_type) endend
Always use request.find_type for type lookups - it’s cached during the request and respects namespaces.
Unions require spreads to access fields since they don’t define any themselves:
query_fields do field :search, 'SearchResult', array: true do argument :query, :string, null: false endenddef search(query:) # Returns mixed array of Posts, Comments, and Users GlobalSearch.perform(query)end
Query with Spreads
query { search(query: "graphql") { __typename ... on Post { id title body } ... on Comment { id text author { name } } ... on User { id email username } }}
The __typename field is the only field you can query directly on a union. Use spreads for everything else.
union 'CreateUserResult', of_types: %w[User ValidationError]mutation_fields do field :create_user, 'CreateUserResult', null: false do argument :input, 'UserInput', null: false endenddef create_user(input:) user = User.new(input.params) if user.save user else ValidationError.new(errors: user.errors) endend