The most frequently asked questions in DevRel when I started with Contentful were about how to display links or linked entries and assets inside the Contentful Rich Text field on the front end. It’s no secret that those of you who tuned in to my Twitch streams after I had started at Contentful saw me struggle with the concept of links as well!
So, I set out to explore and investigate the inner workings of the Contentful REST API and GraphQL API in terms of linking assets and entries on a content type in order to understand how we can render links inside Contentful Rich Text fields.
What are links in Contentful?
Links are Contentful’s way of modelling relationships between content types and entries. Entries in Contentful can contain link fields that point to other assets or entries, and those entries can link to other assets or entries, and so on. For example:
A blog post can have an author
A team can have many authors
A company can have many teams
You can liken this to working with relational databases, where you would define one to one or one to many relationships within your data structures or models. For more information on the concept of links in Contentful, visit the documentation.
Here’s the content model that we’ll be working with in this article. The screenshot of the blog post content model below shows that the Author field is a Reference field type, which is a link.
If you’re using the GraphQL API, you control how your entries are resolved in the construction of your GraphQL query. And by understanding how the REST API works and how the SDKs resolve links, you’ll be all set.
Let’s take a look!
Requesting data from Contentful
Using the REST API
Take this example request URL.
It is querying the Contentful Delivery API with the following parameters:
spaceId: Our space ID
accessToken: Our access token for the Content Delivery API
Return the blogPost entry that has this slug
Return linked entries and assets up to 10 levels deep (this is the maximum
includeparameter value on the Content Delivery API) - we will unpack this later!
1. The REST API response
The raw JSON response from the request above contains the following top level properties and nodes in a flat structure.
2. The items array
items contains the requested entries (the entry with the matching slug in this case). Each entry contains a subset of the
fields defined on the content type of this entry and some internal system information (
sys). Notice how the linked
author entry is missing the
fields property. It only holds the
sys information including the
Where are the author fields? Let’s find out!
3. The includes object
includes object contains two array nodes:
"Entry"for all referenced entries in
items(such as the the blog post author which we saw returned as a
“type”: “Link”in the response above)
"Asset"for all referenced assets in
items(such as images, which might be a featured image on a blog post, for example)
In the case of the
author, which is a linked entry on our
blogPost, we see the full author object returned in
includes.Entry — including another link to an image asset.
The response includes all the data that you need to render the blog post to the front end. However, the data is spread across
includes, and you — as a developer — would expect all that data to be returned as one object, right? 🤯
For example, in React, you might want to do something like this to show the author’s name on the front end:
Currently, the blogPost item references the author by
You could cross-reference the
items.fields.author.sys.id with the
includes.Entry array, find the item in the array that has the
id that matches, and resolve the data from there. It sounds pretty straightforward in this example, but when your content model gets more complex with many entries linking to other entries, it could get unwieldy.
4. Unpacking the
include request parameter
Specify the depth of the resolved tree using the
include parameter in the request to the API, either as a parameter on the GET request URL, like this:
Both examples above make the same request to the Contentful API — except the SDK example is resolving your linked entries as part of the process using contentful-resolve-response. Neat!
5. How the
include parameter affects the length of the
Say you have a blog post, which has a reference to an author, which has a reference to a team.
To visualise this in an object graph:
If you specify
includes=1 in your request, your
includes array on the response will contain one item in this example, the
author object (1 level deep).
If you specify
includes=2 in your request, your
includes array on the response will contain two items, the
author object and the
team object. (2 levels deep).
blogPost had another top level reference, say a
includes=1 would return both the
heroBanner inside the
Regardless of the
include depth you specify — the SDK — which uses the contentful-resolve-response package, will link all available and responded entries and assets that are returned in the
Using the GraphQL API
Our very own GraphQL Content API doesn’t require an SDK to handle linked entries — but understanding the concepts covered previously helps us out here.
1. The main differences between the REST API and GraphQL API
The response from the GraphQL API gives you a rich object graph as standard (so you won’t find
includesin the response).
With GraphQL you specify the equivalent depth of the
includesresponse through the construction of your query. The only limit here is the complexity of your GraphQL query. Technically, if you construct your query cleverly, you can reach data hundreds of levels deep! Read more about GraphQL complexity limits here.
Here’s the GraphQL query that we would use to fetch the same blog post data with the author name and image as referenced in the first example:
And here’s how we can query the Contentful GraphQL API using fetch:
To compare this query to the
include levels in the REST API:
2. The GraphQL API response
Due to how we constructed our GraphQL query to fetch the linked entries and assets, the raw response from the GraphQL contains the data for the linked assets and entries in the nodes we would expect — at a content type level only.
Here’s the response for the above query from the GraphQL API:
In the response above, the data for
author appeared in the node tree exactly where we expected it, and we can access the name on the front end — for example, via
data.blogPostCollection.items.author.name — without having to use an SDK to resolve the entries.
3. The include depth is inferred by the construction of your GraphQL query
In comparison to the REST API, where you usually fetch the blog post data and link the entries after the fact, a GraphQL API query is entirely flexible to your needs. There’s always the caveat, however, that a complex GraphQL query with many nested link assets and entries might surpass the maximum complexity permitted on the GraphQL API. Read more about GraphQL complexity limits here.
Understanding the structure of the data responses from Contentful and how linked assets are returned and then resolved via the Contentful SDKs, empowers you to choose which APIs and methods are best suited to your applications.
And, hey, if you want to resolve the linked assets and entries yourself, then you’re well equipped.
Check out some further reading on how you can resolve linked assets and entries from the Contentful Rich Text field response in both the REST API and GraphQL API. If you're enjoying the experience of using Contentful, share the good news with your network so they can can sign up for a free Contentful account (no credit card required).
And remember, build stuff, learn things and love what you do.