Unions

A GraphQL union represents a set of object types that share no required common fields. Unlike interfaces, union members do not need to declare the same fields. Clients use inline fragments to select fields from each possible type.

GraphQL schema

GraphQL
type TextContent {
  text: String!
}

type ImageContent {
  imageUrl: String!
  height: Int!
}

union PostContent = TextContent | ImageContent

Client query

GraphQL
{
  content {
    ... on TextContent {
      text
    }
    ... on ImageContent {
      imageUrl
    }
  }
}

Defining a Union Type#

Use a marker interface (an interface with no members) or an abstract class to group the types that belong to the union.

Union vs Interface#

Both unions and interfaces are abstract types that let a field return one of several object types. They differ in how much structure they enforce and how clients query them.

Use a union when the member types are genuinely different entities with no meaningful shared fields, for example a search that can return a User, a Post, or a Comment. Clients must use inline fragments for every field because the union guarantees no common structure.

GraphQL
union SearchResult = User | Post | Comment

# Client must fragment into each type
query {
  search(term: "graphql") {
    ... on User {
      name
    }
    ... on Post {
      title
    }
    ... on Comment {
      body
    }
  }
}

Use an interface when the types share common fields that clients regularly query together. The interface enforces a contract: every implementing type must include the interface fields. Clients can query those fields directly without fragments.

GraphQL
interface Event {
  id: ID!
  timestamp: DateTime!
}

# Shared fields are queryable directly
query {
  events {
    id
    timestamp
    ... on UserEvent {
      user {
        name
      }
    }
    ... on SystemEvent {
      severity
    }
  }
}

Use both together when you need the flexibility of a union with some guaranteed fields across members. The errors-as-data pattern is a common example: a union separates success from failure, while an interface guarantees a message field on all error types.

GraphQL
interface CheckoutError {
  message: String!
}

type InsufficientStockError implements CheckoutError {
  message: String!
  availableStock: Int!
}

type InvalidPaymentError implements CheckoutError {
  message: String!
}

union CheckoutResult = Order | InsufficientStockError | InvalidPaymentError

Next Steps#

Edit this page on GitHub
Last updated on by Tobias Tengler