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);
}
}
}
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