代码之家  ›  专栏  ›  技术社区  ›  Simon H

使Graphql模式干燥[重复]

  •  0
  • Simon H  · 技术社区  · 7 年前

    我是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
      }
    

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

    type Mutation {
        addPizza(input:Pizza):FoodType
        addSalad(input:Salad):FoodType
        addBasic(input:BasicFood):FoodType
    }
    

    这不起作用有两个原因:

    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
    }
    

    所以,在这里,我定义了披萨、沙拉和基本食物的3种制作方法。 我需要定义3种输入类型(每种食物一种) 我还需要为配料定义一种新的输入类型。

    这会造成很多重复。你同意吗?还是有更好的方法来解决这个问题?

    非常感谢。

    0 回复  |  直到 8 年前
        1
  •  5
  •   Daniel Rearden    7 年前

    你需要做的事情有很多 能够 做例如,如果要以编程方式声明模式,可以这样做:

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

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

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

    或者,当使用 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]
      }
    `
    

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

    同样重要的是要理解,虽然在语法上,类型和输入类型可能看起来相同,但在功能上却不同。例如,类型上的字段可能有参数:

    type Pizza {
      toppings(filter: ToppingTypeEnum): [String]
    }
    

    输入类型字段没有参数,因此您将无法对输入类型使用相同的语法 toppings 在两个 Pizza 类型及其对应项 PizzaInput 输入类型。

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

        2
  •  0
  •   valerii15298    5 年前

    我用的是Dgraph,它解决了同样的问题。 看看: https://dgraph.io/docs/graphql/schema/types/#interfaces 基本上,如果使用dgraph,只需编写少量代码,它就会生成rest。 还有webstorm,你可以写:
    # noinspection GraphQLInterfaceImplementation 在文件顶部,它将禁用对接口的检查,然后使用某种transpiler在使用接口的类型上填充字段。

    推荐文章