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
type TextContent {
text: String!
}
type ImageContent {
imageUrl: String!
height: Int!
}
union PostContent = TextContent | ImageContentClient query
{
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.
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.
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.
interface CheckoutError {
message: String!
}
type InsufficientStockError implements CheckoutError {
message: String!
availableStock: Int!
}
type InvalidPaymentError implements CheckoutError {
message: String!
}
union CheckoutResult = Order | InsufficientStockError | InvalidPaymentErrorNext Steps#
- Need shared fields across types? See Interfaces.
- Need to define output types? See Object Types.
- Need input polymorphism? See OneOf Input Objects.