首页 > 解决方案 > 为什么子字段在不同的接口时会发生冲突?

问题描述

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

LightningRoundQuestion.answer并且PictureRoundPicture.answer应该返回一个String,并且MultipleChoiceRoundQuestion.answer应该返回一个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."

不太确定下一步该做什么,我查看了 Apollo 文档中的 Alias,但那里几乎没有帮助。

标签: node.jsgraphqlapollographql-jsapollo-server

解决方案


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

有关详细说明,请参阅规范的此部分

有两种解决方法。在客户端,您可以使用别名来确保 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
    }
  }
}

请注意,我们不必使用type,themepointValue因为这些字段在您的所有实现类型中具有相同的类型。


推荐阅读