Use GraphQL to retrieve an object that contains an array of objects with different schemas
Asked Answered
G

2

23

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?

Gulick answered 15/4, 2019 at 22:25 Comment(0)
W
40

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".

Wilkins answered 15/4, 2019 at 23:42 Comment(3)
This was exactly what I was looking for! Thank you Daniel! 🙌Gulick
Brilliant, was stuck on similar issue for a couple of hours. @ChristophAnderson should accept this answer.Linda
so graphql can't tell the user what its returning. the user has to guess. who invented this trash logic?Pen
K
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 }
        }
    }
}
Kadi answered 15/4, 2019 at 23:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.