Structurize your GraphQL implementation with fragments and unions

Berry de Witte
wehkamp-techblog
Published in
3 min readFeb 15, 2021

In some cases, an API could return an array containing multiple different values. In our case, we use Contentful for all our content related pages where we can have multiple different unrelated content models to build up a page. From a Contentful way of working this works fine, but when connecting to GraphQL we ran into some questions. How will you define your query, schemas, type definitions and keep it all structured?

Using fragments in the query

Our first approach was adding all the possible properties to the items query wherein the end we got a list of all possible properties and no clue which property belongs to which content model.

// just a small part of the properties in our items array
items {
filters
id
imageUrl
link
name
overlayUrl
productNumbers
recommenderType
subtitle
title
type
}

The more structured way is to split up the query to match the models which we defined in Contentful so we created afragment matching every model.

fragment RecommenderFragment on Recommender {
id
recommenderType
type
}
fragment TileFragment on Tile {
id
imageLink
imageUrl
subtitle
title
type
}
fragment ProductNumbersFragment on ProductNumbers {
id
productNumbers
filters
type
}

It’s a little bit more boiler plating, but it gives a better view of your data. You could even create a basic fragment for recurring properties like id and type.

Using unions in the type definitions

So we split up our query with fragments, but how to structurize the type definitions? How can you explain that the items can be either one of Recommender, Tile, or ProductNumbers? The answer here is to use the union statement. You can create your type definitions like you usually would do and only add the union statement to declare of which types it can be.

union Item = Recommender | Tile | ProductNumberstype Content {
items: [Item]
}
type Recommender {
id: ID!
recommenderType: String
type: String!
}
type Tile {
id: ID!
imageUrl: String
imageUrl: String
subtitle: String
title: String
type: String!
}
type ProductNumbers {
id: ID!
productNumbers: [String]
filters: [String]
type: String!
}

Resolving the correct type

After we split up the query and type definitions it’s time to tell the resolver to which type it should resolve the data. We based our resolving on the type we send with each item in our data where it matches our types in the type definitions exactly. You could also do some manual matching here based on other properties in your data.

Item: {
__resolveType: obj => obj.type,
},

Combining queries with unions

Then the final step is updating the query we started with to work with both the created fragments and union statement.

items {
// defines the union
... on Recommender {
// spread in the fragment
...RecommenderFragment
}
... on Tile {
...TileFragment
}
... on ProductNumbers {
...ProductNumbersFragment
}
}

It’s a little bit more work and some more coding in the end, but in return, you’ll get a nicely structured setup of your data.

--

--