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);
}
}
}
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.
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.
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).
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") { ...
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With