GraphQL如何避免输入和输出类型之间的重复代码

Fre*_*iot 8 interface apollo graphql

我是GraphQL的新手,但我真的很喜欢它.现在,我正在玩接口和工会,我面临着突变的问题.

假设我有这个架构:

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
  }
Run Code Online (Sandbox Code Playgroud)

现在,我想创造新的食品,所以我开始做这样的事情:

type Mutation {
    addPizza(input:Pizza):FoodType
    addSalad(input:Salad):FoodType
    addBasic(input:BasicFood):FoodType
}
Run Code Online (Sandbox Code Playgroud)

这不起作用有两个原因:

  1. 如果我想将对象作为参数传递,则该对象必须是"输入"类型.但"披萨","沙拉"和"基本食品"只是"类型".
  2. 输入类型无法实现接口.

所以,我需要修改我之前的架构,如下所示:

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
}
Run Code Online (Sandbox Code Playgroud)

所以,在这里我为Pizza,Salad和Basic食物定义了我的3种创作方法.我需要定义3种输入类型(每种食物一种)我还需要为Ingredients定义一个新的输入类型.

它会造成很多重复.那样你觉得可以吗?或者有更好的方法来解决这个问题?

谢谢

Dan*_*den 5

可以做一些事情。例如,如果您要以编程方式声明架构,则可以使用以下方法:

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)
})
Run Code Online (Sandbox Code Playgroud)

或者,如果您的对象/输入遵循类似的模式,您甚至可以编写一个函数来从类型生成输入:

const transformObject = (type) => {
  const input = Object.assign({}, type)
  input.fields.composition.type = new GraphQLList(IngredientInput)
  delete input.fields.id
  return input
}
Run Code Online (Sandbox Code Playgroud)

或者,在使用 定义架构时makeExecutableSchema,您可以执行以下操作:

const commonPizzaFields = `
    type: String
    pizzaType: String
    toppings: [String]
    size: String
`

const schema = `
  type Pizza {
    id: String
    ${commonPizzaFields}
    composition: [Ingredient]
  }

  input PizzaInput {
    ${commonPizzaFields}
    composition: [IngredientInput]
  }
`
Run Code Online (Sandbox Code Playgroud)

所有这些方法的问题在于,虽然它们在技术上可能会使您的代码更加 DRY,但它们也会降低您的架构的可读性,在我看来,这使得它比重复本身更容易出错。

同样重要的是要了解,虽然在语法上,Type 和 Input 类型可能看起来相同,但在功能上却并非如此。例如,类型上的字段可能有参数:

type Pizza {
  toppings(filter: ToppingTypeEnum): [String]
}
Run Code Online (Sandbox Code Playgroud)

输入类型字段没有参数,因此您将无法对类型及其对应输入类型中的toppings字段使用相同的语法。PizzaPizzaInput

就个人而言,我会咬紧牙关,像您已经完成的那样写出类型和输入。我唯一会做的不同是将它们组合在一起(列出您的类型而不是您的输入),以便很容易发现两者之间的任何差异。但是你的里程可能会很大:)