How to resolve union/interface fields with GraphQL and ApolloStack

JustinN picture JustinN · Jan 13, 2017 · Viewed 14.7k times · Source

I am making use of interfaces with my GraphQL instance, but this question perhaps applies to unions as well. There are 2 common fields across all types which implement the interface, however there are multiple additional fields on each type.

Given the following schema

interface FoodType {
  id: String
  type: String
}

type Pizza implements FoodType {
  id: String
  type: String
  pizzaType: String
  toppings: [String]
  size: String
}

type Salad implements FoodType {
  id: String
  type: String
  vegetarian: Boolean
  dressing: Boolean
}

type BasicFood implements FoodType {
  id: String
  type: String
}

and the following resolvers

{
  Query: {
    GetAllFood(root) {
      return fetchFromAllFoodsEndpoint()
        .then((items) => {
          return mergeExtraFieldsByType(items);
        });
    },
  },
  FoodType: {
    __resolveType(food) {
      switch (food.type) {
        case 'pizza': return 'Pizza';
        case 'salad': return 'Salad';
        default: return 'BasicFood';
      }
    },
  },
  Pizza: {
    toppings({pizzaType}) {
      return fetchFromPizzaEndpoint(pizzaType);
    }
  }
}

How do I obtain the additional fields for each type?

Currently, I have the allFood fetching all foods to obtain the basic fields of id and type. After this I am looping over the results, and if any of found of the type Pizza, I make a calling to fetchFromPizzaEndpoint, obtaining the additional fields and merging those onto the original basic type. I repeat this for each type.

I am also able to manually resolve specific fields, one at a type, such as the Pizza.toppings, as seen above.

Now my solution is not ideal, I would much rather be able to resolve multiple fields for each type, much the same way I do with the single field toppings. Is this possible with GraphQL? There must be a better way to achieve this, seeing as it's quite a common use case.

Ideally, I would like to be able to know in my resolver, what fragments my query is asking for, so I can only make calls to endpoints which are asked for (one endpoint per fragment).

{
  Query: {
    GetAllFood(root) {
      return fetchFromAllFoodsEndpoint();
    },
  },
  FoodType: {
    __resolveType(food) {
      switch (food.type) {
        case 'pizza': return 'Pizza';
        case 'salad': return 'Salad';
        default: return 'BasicFood';
      }
    },
  },
  Pizza: {
    __resolveMissingFields(food) {
      return fetchFromPizzaEndpoint(food.id);
    }
  },
  Salad: {
    __resolveMissingFields(food) {
      return fetchFromSaladEndpoint(food.id);
    }
  }
}

Answer

Sean Ryan picture Sean Ryan · Jun 30, 2017

I know this question is 5 months old, but I hope this helps anyone else with this problem. He is passing his resolvers structured like

{
    Query: {
        GetAllFood(root) {
        return fetchFromAllFoodsEndpoint()
            .then((items) => {
            return mergeExtraFieldsByType(items);
            });
        },
    },
    FoodType: {
        __resolveType(food) {
        switch (food.type) {
            case 'pizza': return 'Pizza';
            case 'salad': return 'Salad';
            default: return 'BasicFood';
        }
        },
    },
    Pizza: {
        toppings({pizzaType}) {
        return fetchFromPizzaEndpoint(pizzaType);
        }
    }
}

But he really wanted something like (not exactly, but I'm stressing the location of __resolveType relative to Query)

{
    Query: {
        GetAllFood(root) {
        return fetchFromAllFoodsEndpoint()
            .then((items) => {
            return mergeExtraFieldsByType(items);
            });
        },
    },
    FoodType: {
        __resolveType(data, ctx, info) {
            return whatIsTheType(data, ctx, info)
        }
    }
}

The official documentation has an example here, but it ONLY includes the interface type, which I found confusing. I have an additional complete runnable example of Union types (configured identically to interfaces) available here