Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to resolve union/interface fields with GraphQL and ApolloStack

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);
    }
  }
}
like image 358
JustinN Avatar asked Jan 13 '17 08:01

JustinN


People also ask

How do you resolve unions in GraphQL?

Resolving a union To achieve this, you define a __resolveType function for the union in your resolver map. The __resolveType function is responsible for determining an object's corresponding GraphQL type and returning the name of that type as a string.

Can an interface implement another interface GraphQL?

Only types can implement an interface. An interface cannot implement another interface. You can see the syntax for interfaces defined here, which distinctly lacks the ImplementsInterfaces definition shown here.

Is it possible to use inheritance with GraphQL input types?

No, the spec does not allow input types to implement interfaces. And GraphQL type system in general does not define any form of inheritance (the extends keyword adds fields to an existing type, and isn't for inheritance).

What is Union in GraphQL?

A union type is a set of object types which may appear in the same spot. Here's a union, expressed in GraphQL Schema Definition Language (SDL): union MediaItem = AudioClip | VideoClip | Image | TextSnippet. This might be used on a search field, for example: searchMedia(term: "puppies") { ...


1 Answers

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

like image 153
Sean Ryan Avatar answered Nov 13 '22 05:11

Sean Ryan