I felt it would be useful to talk through some of this code in more detail to explain what is happening, and why.
Firstly, we're using data from Kontent to generate our schema, therefore you will need to register for a new account on their website. Once you have registered, take some time to create some content models and a variety of example content items based on those models. After you have some content to work with you can continue on to the next step.
We now need to retrieve the data from Kontent's API, we can do this using their Delivery SDK. This can be installed by running:
A new client can be created with your project ID:
The Kontent models and items can be retrieved using the client with the following code:
We can now use this data to create our Gatsby schema!
Firstly, we create a whole bunch of base GraphQL types to ensure we have shared types available for fields and also common interfaces to make querying some of this data much easier! All of the base types can be found in the createBaseTypes function.
Once we've defined these shared GraphQL types we can start declaring the schema for our actual content models.
We will retrieve the types from the Kontent API then reduce those into an array of types - note we use a reduce function rather than a map function as we're returning multiple types from the "createTypeDef" function and need to flatten the result. The same result could have been achieved by using a map function and simple flattening the resulting nested array. The final array of type definitions is passed to Gatsby's "createTypes" function.
The actual type definition is handled by the createTypeDef function.
Here we use the new Type Builders API to construct a new GraphQL type. We try to mimic the general structure of the Kontent API by including both a top-level "system" and "elements" field. The "system" field is configured to use the "KontentItemSystem" type that we created earlier in the createBaseTypes function. This will be the same for all content models.
The "elements" field is configured to use a model-specific GraphQL type as the available fields will be different for each content model.
Firstly, we reduce the elements array into an object that includes the correct type for each field. This is done based on convention, the field name is generated by converting the Kontent codename value into camel case.
The element's GraphQL type is defined by converting it's Kontent field type into pascal case and sandwiching it between the keywords "Kontent" and "Element", e.g. "KontentTextElement" for a "text" field.
We then create a new type for the "elements" field itself with inference disabled using the "infer" property on the type builder definition.
Finally, we create the actual type for our content model by defining the name using pascal case conversion from the codename value and specifying the fields mentioned earlier. Finally, we ensure our type implements both the "Node" and "KontentItem" interfaces. The former is the interface all Gatsby node types must implement to ensure they can be queried. The latter is a custom interface we defined to provide a common contract between all of our content models, this can be handy when querying fields later or when creating connections between types via the "@link" directive.
At this stage you could run your Gatsby site and check the schema using GraphiQL, you would notice that all of our types exist but if you execute a query you will see no data returned. Now we can look at fixing that!
First of all we need to retrieve our content items from the Kontent API. This includes two collections of items on the "items" and "linkedItems" properties. We don't really care about the difference between these two so we can simply combine these arrays and union them based on the "system.codename" property to ensure we do not have any duplicates.
Finally, we will create a Gatsby node for each content item and pass that to Gatsby's "createNode" function. The node creation is handled by the "createItemNode" function.
Finally, we can run the Gatsby site again and you should now be able to see data returned when executing your queries. The great thing about this approach is that if you delete all items of a particular model, or if none of the items have data for a particular field, then you'll be safe in the knowledge that your queries will still work as intended!