Chapter 6 GraphQL: Interfaces and Unions

6.1 Introduction

An interface exposes a certain set of fields that a type must include to implement the interface.

schema.graphql

interface Restaurant {
    id:ID!
    name: String!
}

type Indian implements Restaurant{
    id:ID!
    name: String!
    brewedBeer:Boolean!
}

type Burger implements Restaurant{
    id:ID!
    name: String!
    vegetarianOptions: Boolean!
}

type Query{
    listRestaurants: [Restaurant!]
}

Unions are identical to interfaces, except that they don’t define a common set of fields. Unions are generally preferred over interfaces when the possible types do not share a logical hierarchy.

union Item = Food | Electronic | Customer

type Electronic {
    size: Float
    weight: Float
}

type Food {
    family: String
}

type Customer {
    fullName: String
    zip: String
}
type Query{
    listItems: [Item!]
}
 

6.2 Fragments

Fragments are powerful technique when we are consuming a query that returns an Interface or an Union. They are used to define what attributes we want to obtain from the server depending on the type of the concrete element.

query {
    listRestaurants:{
        id
        name
        ... on Indian {
            brewedBeer
        }
        ... on Burger {
            vegetarianOptions
        }
        __typename
    }
}  

6.3 Code

Unions and interfaces can be easily represented in Java since the language provides us with abstract classes and interfaces. The below pieces of code show an example for the GraphQL definitions in the previous point.

com.wesovilabs.workshops.graphql.domain.Restaurant.java

public class Restaurant {
    
    private String id;
    
    private Integer name;
}

com.wesovilabs.workshops.graphql.domain.Indian.java

public class Indiand extends Restaurant {
    private Boolean brewedBeer;
}

com.wesovilabs.workshops.graphql.domain.Burger.java

public class Burger extends Restaurant {
    private Boolean vegetarianOptions;
}

com.wesovilabs.workshops.graphql.resolver.QueryResolver.java

@Component
public class QueryResolver implements GraphQLQueryResolver {
    
    public List<Restaurant> listRestaurants() {
        List<Resturant> restaurants = new ArrayList<Restaurant>();
        restaurants.addAll(indianService.listAll());
        restaurants.addAll(burgerService.listAll());
        return restaurants;
    } 
}

For unions implementations, the classes should implement an Interface instead of extending a class.

com.wesovilabs.workshops.graphql.domain.Item.java

public interface Item{
    
}

com.wesovilabs.workshops.graphql.domain.Electronic.java

public class Electronic implements Item{
    private Float size;
    private Float weight;
}

com.wesovilabs.workshops.graphql.domain.Customer.java

public class Customer implements Item{
    private String name;
}

com.wesovilabs.workshops.graphql.resolver.QueryResolver.java

@Component
public class QueryResolver implements GraphQLQueryResolver {
    
    public List<Item> listItems() {
        List<Item> items = new ArrayList<Item>();
        items.addAll(electronicService.listAll());
        items.addAll(customerService.listAll());
        return items;
    } 
}

6.4 Challenges

  • Define an interface Person with commons attributes for Actor and Director. Add a new query listPeople that returns a list of people ([Person!]).
query{
    listPeople:[Person!]
}

Once you’ve implemented this query make use of fragments to return the below details

{
  "data": {
    "listPeople": [
      {
        "__typename": "Actor",
        "fullName": "Johnny Depp",
        "gender": "female"
      },
      ...
      {
          "__typename": "Director",
          "fullName": "Steven Spielberg",
          "country": "USA"
       }
       ...
    ]
  }
}
  • Define an union named Item that could be a Movie or an Actor. Add an operations listItems that return the full list of Items. [Item!]
query{
    listItems:[Item!]
}

Once you’ve implemented this query make use of fragments to return the below details

{
  "data": {
    "listItems": [
      {
        "__typename": "Movie",
        "title": "Edward Scissorhands"
      },
      ...
      {
        "__typename": "Actor",
        "fullName": "Russell Crowe"
      }
      ...
    ]
  }
}