为什么子字段在不同界面时会发生冲突?

Why do subfields conflict when they are in different interfaces?

我正在尝试使用 GraphQL (Apollo Server) 查询单个 MongoDB 文档 (trivia),但我在处理其中一个文档字段时遇到问题。

LightningRoundQuestion.answerPictureRoundPicture.answer 应该 return 和 StringMultipleChoiceRoundQuestion.answer 应该 return 和 Int。查看架构:

schema

const typeDefs = gql`
  # ROOT TYPES ==================================================
  type Query {
    trivia(_id: String!): Trivia
  }

  # INTERFACES ==================================================
  interface Round {
    type: String!
    theme: String!
    pointValue: Int!
  }

  type LightningRound implements Round {
    type: String!
    theme: String!
    pointValue: Int!
    questions: [LightningRoundQuestion]
  }

  type MultipleChoiceRound implements Round {
    type: String!
    theme: String!
    pointValue: Int!
    questions: [MultipleChoiceRoundQuestion]
  }

  type PictureRound implements Round {
    type: String!
    theme: String!
    pointValue: Int!
    pictures: [PictureRoundPicture]
  }

  # QUERY TYPES =================================================
  type LightningRoundQuestion {
    question: String!
    answer: String!
  }

  type MultipleChoiceRoundQuestion {
    question: String!
    options: [String!]!
    answer: Int!
  }

  type PictureRoundPicture {
    url: String!
    answer: String!
  }

  type Trivia {
    _id: String!
    createdAt: String!
    triviaId: String!
    triviaPin: String!
    host: String!
    rounds: [Round]!
    tieBreaker: TieBreaker!
  }

  type TieBreaker {
    question: String
    answer: Int
  }
`

resolvers & server

const resolvers = {
  Query: {
    trivia: async (root, { _id }) => {
      return triviaCollection.findOne(ObjectId(_id))
    }
  },
  Round: {
    __resolveType(obj) {
      if (obj.type === 'multipleChoice') {
        return 'MultipleChoiceRound'
      } else if (obj.type === 'lightning') {
        return 'LightningRound'
      } else if (obj.type === 'picture') {
        return 'PictureRound'
      }
    }
  }
}

const server = new ApolloServer({ typeDefs, resolvers })

server.listen().then(({ url }) => {
  console.log(`Server ready at ${url}`)
})

query

query {
  trivia(_id: "5e827a4e1c9d4400009fea32") {
    _id
    createdAt
    triviaId
    triviaPin
    host
    rounds {
      ... on LightningRound {
        questions {
          question
          answer
        }
      }
      ... on MultipleChoiceRound {
        questions {
          question
          options
          answer
        }
      }
      ... on PictureRound {
        pictures {
          url
          answer
        }
      }
    }
  }
}

我收到错误消息:

"message": "Fields \"questions\" conflict because subfields \"answer\" conflict because they return conflicting types \"String!\" and \"Int!\". Use different aliases on the fields to fetch both if this was intentional."

不太确定下一步该做什么,我查看了 Alias in the Apollo documentation,但没有什么帮助。

这在使用抽象类型时有点棘手,并且由于 GraphQL 如何处理合并字段选择而发生。归结为,如果您在同一选择集中多次请求同名(或别名)的字段,则该字段必须 return 相同类型。在上面的模式中,它不是——问题可以有 [LightningRoundQuestion][PictureRoundPicture] 等类型。

有关详细说明,请参阅规范的 this section

对此有两种解决方法。在客户端,您可以使用别名来确保 GraphQL 不会首先尝试合并字段:

rounds {
  ... on LightningRound {
    lightningQuestions: questions {
      question
      answer
    }
  }
  ... on MultipleChoiceRound {
    multipleChoiceQuestions: questions {
      question
      options
      answer
    }
  }
}

当您无法更改架构时,这是您最好的选择。但是,您也可以只更改服务器端的字段名称,以获得更好的客户端 DX。

rounds {
  ... on LightningRound {
    lightningQuestions {
      question
      answer
    }
  }
  ... on MultipleChoiceRound {
    multipleChoiceQuestions {
      question
      options
      answer
    }
  }
}

请注意,我们不必对 typethemepointValue 执行此操作,因为这些字段在您的所有实施类型中具有相同的类型。