24

I am trying to write a query to retrieve an object with the property linkedCards that contains an array of objects with different schemas.

I have 3 different schemas (built in Contentful):

CardA example:

{
    id: 42,
    productName: 'Laptop',
    price: 999
}

CardB example:

{
    id: 999,
    title: 'Buy our refurbished Laptops today!'
}

CardC example:

{
    id: 100,
    linkedCards: [
        {
            id: 42,
            productName: 'Laptop',
            price: 999
        },
        {
            id: 999,
            title: 'Buy our refurbished Laptops today!'
        }
    ]
}

Query:

allCardC() {
    nodes {
        linkedCards {
            id
            title
        }
    }
}

When I try to run the following GraphQL query I get "Cannot query field "title" on type "CardACardBUnion". Did you mean to use an inline fragment on "CardA" or "CardB"?"

Is there a built-in way to do this or can I use the ids of CardA & CardB somehow? Perhaps have one query to get the ids of the cards in linkedCards and another query to get said cards?

2 Answers 2

41

As the error indicates, you need to use an inline fragment when querying a field that resolves to a union:

allCardC {
    nodes {
        linkedCards {
            ... on CardA {
              id
              productName
              price
            }
            ... on CardB {
              id
              title
            }
        }
    }
}

Fragments can be defined inline within a selection set. This is done to conditionally include fields based on their runtime type.

Unlike interfaces or regular object types, unions do not specify any particular fields, only the types that make up the union. That means a selection set for a field that returns a union must always use fragments to conditionally specify the fields depending on the actual type that the field resolves to.

It's like saying, "if this is the actual type of the returned object, request these fields".

Sign up to request clarification or add additional context in comments.

3 Comments

This was exactly what I was looking for! Thank you Daniel! 🙌
Brilliant, was stuck on similar issue for a couple of hours. @ChristophAnderson should accept this answer.
so graphql can't tell the user what its returning. the user has to guess. who invented this trash logic?
8

You may find it useful to use a GraphQL interface to specify the fields that every card type has in common.

interface Card {
  id: ID!
}
# type CardA implements Card { ... }
type CardB implements Card {
  id: ID!
  title: String!
}
type CardC implements Card {
  id: ID!
  linkedCards: [Card!]!
}

As @DanielRearden's answer suggests you still need to use (inline) fragments to select fields that are specific to one of the card types, but now that you know every card has an id field, you can select that directly.

allCardC {
    nodes {
        linkedCards {
            id
            ... on CardB { title }
        }
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.