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

Sam*_*rko 2 node.js apollo graphql graphql-js apollo-server

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

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

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

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

我收到错误消息:

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

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

Dan*_*den 5

在使用抽象类型时,这是一个有点陷阱,并且是由于 GraphQL 处理合并字段选择的方式而发生的。归根结底,如果您在同一选择集中多次请求具有相同名称(或别名)的字段,则该字段必须返回相同的类型。在上面的架构中,它没有 - 问题可以具有类型[LightningRoundQuestion], or[PictureRoundPicture]等​​等。

有关详细说明,请参阅规范的这一部分。

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

rounds {
  ... on LightningRound {
    lightningQuestions: questions {
      question
      answer
    }
  }
  ... on MultipleChoiceRound {
    multipleChoiceQuestions: questions {
      question
      options
      answer
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

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

rounds {
  ... on LightningRound {
    lightningQuestions {
      question
      answer
    }
  }
  ... on MultipleChoiceRound {
    multipleChoiceQuestions {
      question
      options
      answer
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

请注意,我们不必使用 ,或来执行此操作type,因为这些字段在所有实现类型中具有相同的类型。themepointValue