I'am new to GraphQL but I really like it. Now that I'am playing with interfaces and unions, I'am facing a problem with mutations.
Suppose that I have this schema :
interface FoodType {
id: String
type: String
composition: [Ingredient]
}
type Pizza implements FoodType {
id: String
type: String
pizzaType: String
toppings: [String]
size: String
composition: [Ingredient]
}
type Salad implements FoodType {
id: String
type: String
vegetarian: Boolean
dressing: Boolean
composition: [Ingredient]
}
type BasicFood implements FoodType {
id: String
type: String
composition: [Ingredient]
}
type Ingredient {
name: String
qty: Float
units: String
}
Now, I'd like to create new food items, so I started doing something like this :
type Mutation {
addPizza(input:Pizza):FoodType
addSalad(input:Salad):FoodType
addBasic(input:BasicFood):FoodType
}
This did not work for 2 reasons :
So, I need to modify my previous schema like this :
interface FoodType {
id: String
type: String
composition: [Ingredient]
}
type Pizza implements FoodType {
id: String
type: String
pizzaType: String
toppings: [String]
size: String
composition: [Ingredient]
}
type Salad implements FoodType {
id: String
type: String
vegetarian: Boolean
dressing: Boolean
composition: [Ingredient]
}
type BasicFood implements FoodType {
id: String
type: String
composition: [Ingredient]
}
type Ingredient {
name: String
qty: Float
units: String
}
type Mutation {
addPizza(input: PizzaInput): FoodType
addSalad(input: SaladInput): FoodType
addBasic(input: BasicInput): FoodType
}
input PizzaInput {
type: String
pizzaType: String
toppings: [String]
size: String
composition: [IngredientInput]
}
input SaladInput {
type: String
vegetarian: Boolean
dressing: Boolean
composition: [IngredientInput]
}
input BasicFoodInput {
type: String
composition: [IngredientInput]
}
input IngredientInput {
name: String
qty: Float
units: String
}
So, here I defined my 3 creation methods for Pizza, Salad and Basic food. I need to define 3 input types (one for each food) And I also need to define a new input type for Ingredients.
It makes lot of duplication. Are you ok with that? Or there is a better way to deal with this?
Thank you
There's a handful of things you could do. For example, if you were to declare your schema programatically, you can get away with something like this:
const getPizzaFields = (isInput = false) => {
const fields = {
type: { type: GraphQLString }
pizzaType: { type: GraphQLString }
toppings: { type: new GraphQLList(GraphQLString) }
size: { type: GraphQLString }
composition: {
type: isInput ? new GraphQLList(IngredientInput) : new GraphQLList(Ingredient)
}
}
if (!isInput) fields.id = { type: GraphQLString }
return fields
}
const Pizza = new GraphQLObjectType({
name: 'Pizza',
fields: () => getFields()
})
const PizzaInput = new GraphQLObjectType({
name: 'Pizza',
fields: () => getFields(true)
})
Or if your objects/inputs follow a similar pattern, you could even write a function for generating inputs from types:
const transformObject = (type) => {
const input = Object.assign({}, type)
input.fields.composition.type = new GraphQLList(IngredientInput)
delete input.fields.id
return input
}
Alternatively, when defining your schema using makeExecutableSchema
, you could do:
const commonPizzaFields = `
type: String
pizzaType: String
toppings: [String]
size: String
`
const schema = `
type Pizza {
id: String
${commonPizzaFields}
composition: [Ingredient]
}
input PizzaInput {
${commonPizzaFields}
composition: [IngredientInput]
}
`
The problem with all of these approaches is that while they technically may make your code more DRY, they also reduce your schema's readability, which in my opinion makes it even more error-prone than the duplication itself.
It's also important to understand that while syntactically, a Type and an Input type may appear the same, functionally they are not. For example, a field on a type may have arguments:
type Pizza {
toppings(filter: ToppingTypeEnum): [String]
}
Input Type fields do not have arguments, so you would not be able to utilize the same syntax for a toppings
field in both a Pizza
Type and its counterpart PizzaInput
Input Type.
Personally, I would bite the bullet and just write out both the types and the inputs like you've already done. The only thing I would do differently is grouping them together (listing your type than your input), so that any differences between the two are easy to spot. But your mileage may very :)
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