Reputation: 1600
I am trying to query a single MongoDB document (trivia
) using GraphQL (Apollo Server), but am having trouble with one of the document fields.
LightningRoundQuestion.answer
and PictureRoundPicture.answer
should return a String
, and MultipleChoiceRoundQuestion.answer
should return an Int
. See the schema:
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
}
}
}
}
}
I get the error message:
"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."
Not quite sure what to do next, I looked at Alias in the Apollo documentation, but there's little help there.
Upvotes: 2
Views: 2870
Reputation: 84687
This is a bit of a gotcha when using abstract types and occurs as a result of how GraphQL handles merging field selections. What it boils down to is that if you request a field with the same name (or alias) multiple times inside the same selection set, the field has to return the same type. In the above schema, it doesn't -- questions could have the type [LightningRoundQuestion]
, or [PictureRoundPicture]
and so on.
For a detailed explanation, see this section of the spec.
There's two workarounds for this. On the client side, you can use aliases to ensure GraphQL won't try to merge the fields in the first place:
rounds {
... on LightningRound {
lightningQuestions: questions {
question
answer
}
}
... on MultipleChoiceRound {
multipleChoiceQuestions: questions {
question
options
answer
}
}
}
This is your best bet when you can't change the schema. However, you can also just change the names of the fields on the server-side for better client-side DX.
rounds {
... on LightningRound {
lightningQuestions {
question
answer
}
}
... on MultipleChoiceRound {
multipleChoiceQuestions {
question
options
answer
}
}
}
Notice that we don't have to do that with, type
, theme
or pointValue
because these fields have the same type across all your implementing types.
Upvotes: 5