How to use the Dynamic Content delivery APIs to expose content in your GraphQL server
GraphQL is rapidly becoming the go to pattern for exposing and aggregating data for modern frontend apps. GraphQL helps your app cut down the number of requests it needs to make, avoids under and over fetching of data, and reduces latency and payload sizes. Not to mention the productivity improvements it offers to developers.
To get the most out of GraphQL, it's best to include all of the data sources your app might need in your schema. Headless CMS tools like Dynamic Content make it possible to include content in that mix as we represent content as data which allows it to be sliced and diced in a GraphQL layer. This is an advantage over traditional web CMS solutions as it's much harder to query a fragment of HTML in a similar way.
This article will show how you can consume the Dynamic Content delivery APIs to expose content in your GraphQL server. Doing this will allow your frontends to request content alongside every other data source in your architecture.
In this walkthrough, we are using Apollo, however the same principles apply to other GraphQL implementations.
Before jumping in, take a moment to make sure you have everything you need to follow along.
Content and Content Types
You will also need some content types and content setup in Dynamic Content that we can expose in the GraphQL server. This article will be using the content types from one of the Dynamic Content react samples but you can use any content type you wish.
You can find the code used in this blog on GitHub.
First, we need to create a project and install the dependencies:
Note: If you try to run this you will get an error, don’t worry, you didn’t make a mistake… Apollo requires a valid schema before it will run that we will create in the next section.
Define the GraphQL Schema
GraphQL uses a schema to define the structures and operations the server knows about. Think of this as your API design. Clients will only be able to access the types, fields, queries and mutations you add to your schema.
Dynamic Content also uses a schema to define content types. If we want to expose a content type in our GraphQL server, we need to create a type for it in the GraphQL schema. In most cases, the GraphQL type will very closely resemble the Content Type.
Doing this is relatively easy, you simply need to look at the properties that make up your content type and map them to the equivalent structure in GraphQL. Typically objects will map to a “type” and properties of those objects will map to a “field”.
Keep in mind that the type system in GraphQL is very strict. If a field can be null then don't add the non-null operator "!". This is important to remember as you evolve your content types and add new fields which may not be set in pre-existing content items.
I am going to use this “Navigation” content type, which converts into the following GraphQL schema:
\ Notice that we had to create two types. That is because this particular content type contains a nested object.
Finally, we need to add navigation to the Query type. This is the entry point for fetching the navigation.
Building a Data Source
Next, we need a data access layer that will make API calls to the content delivery API to fetch content. Apollo has some useful “DataSource” classes that you can extend which make it easy to connect to the most common types of data sources such as a REST API or a database.
This data source sets the base URL in the constructor (update this to use your base url) and then exposes a method to lookup content by its delivery key.
Typically, when I use the Content Delivery API, I set the depth query parameter to “all” which tells the API to return any nested content items too, however since this is GraphQL we only need the root item to avoid over fetching. Nested content will be fetched by another resolver (more on this later).
Finally, update your code so that the ApolloServer knows about this new data source.
Now we have a datasource, we can implement the first resolver function for the “navigation” query. Resolvers tell the server how to fetch the data associated with a particular field.
You can see that resolvers have access to the data source we created earlier. This resolver simply fetches the navigation using the delivery key I assigned to the content item in Dynamic Content. A more complicated resolver might take in the key as a parameter.
We are now ready to start the server and perform our first query 🎉.
Resolving Nested Content
This works well for resolving a simple content type, but it is quite common to build more complicated structures by nesting content items together. The code we have so far would only fetch the root item, returning null for any nested items.
Dynamic Content has two types of nested content fields, a “content link” and a “content reference”, which would be resolved in the same way.
To demonstrate how to handle this, we will add another type to the schema called “NavigationSlot”. This Content Type acts as a container for the navigation. It has a field called “navigation” which uses a content link to reference the “Navigation” itself.
First, we need to update the GraphQL schema so that the server knows about the new type and to add a new query for fetching the navigation slot.
As well as adding a new query resolver, we also added a resolver for the field “navigation” on the type “NavigationSlot”. Whenever a query requests the navigation field on a navigation slot, this resolver will be invoked to fetch the nested content.
Apollo gives you access to the “parent” object, in this case the NavigationSlot, which allows us to read the content link id and proceed to fetch it.
After you restart the server, you will be able to query against these new fields.
Handling Union Types
A "NavigationSlot" only ever contains "Navigation" items, but how could we model a Page with lot's a different content?
Let's say we have "Page" that can contain a "HeroBannerBlock", a "GalleryBlock", or an "EditorialBlock". GraphQL can model this with a union type, which allows a single field to have one of a list of possible types.
Step 1: Define the schema
First, we need to model the 3 nested types.
Step 2: Define the resolvers
To resolve a “Page” content type, we need to fetch the page itself and each of the nested components. Instead of lazy loading the nested content like we did previously, I am going to pre-fetch it by setting the content API depth parameter to “all” when loading the page. This would be wasteful if most queries did not ask for the nested content, but in my case, I know the app will always want the nested content when rendering the page.
Update the datasource to take an additional parameter called “depth” that we can pass to the API.
Since the data will be pre-loaded, we don’t need to add a resolver for the “components” field in Page.
Step 3: Resolve the typename
One last hurdle remains. We have all the nested content but the Apollo server has no idea what type each one is. To fix this, we can implement a “resolveType” function.
The resolver below looks at the Content Type of the object and maps it to the appropriate GraphQL type.
Step 4: Query
Once everything is set up, you will be able to perform queries to pull back the page, its content and specify the list of fields you want for each type individually.
You can also reuse these lists of fields with the Fragments feature.
Next Mission 🚀
Congratulations, you now have all the fundamentals covered! In the next article, we will look at ways to optimize the server and solve the N+1 problem, reducing the number of network calls we need to make to process a complex query.