How to include content of referenced stories by resolving relations
In our previous articles, you had the chance to learn how to structure your content for a blog, including authors and categories. The next step is to utilize Storyblok and the already available home content entry to create your landing page and enhance it with featured posts.
The demo content
The default home
content entry that you receive by creating a new space already includes three components: Teaser
, Grid
, and a nested Feature
component. Those components can be removed or adjusted to your needs. What we’re about to do is to extend the list of available components with a new one: featured_posts
.
In this tutorial, we are going to:
- Creating the new
featured_posts
block in the block library - Adding the field named
posts
as a multi-option field. The options are filled with the stories we will create in theposts
folder. We will see how to setup correctly the field - Adding the new
featured_posts
in thehome
story - Retrieving the Home story via API;
- Resolving the relationship via Content Delivery API, GraphQL, and Javascript SDK (also taking into account the Storyblok Bridge)
The featured posts block
The featured posts block will be a container for all our post references. Think of it as a section we want to have on our start page beside a teaser and a grid that contains the information of our articles.
To create the featured post block, we must jump into the Block Library section.
)
Storyblok space block library
Let’s start by navigating to Block Library {1} in the left sidebar. Once you’re on the blocks overview, we will create a new block by pressing New Block {2} on the top right corner.
Then, we can start creating the new block, setting the name, and defining the block type.
)
How to create the nestable block component: featured_posts
In the popup dialog window, fill the name of the block as featured_posts
in the Technical name field {1}, select Nestable block for Select block type option {2}, and click the Add Block button {3} to create the new block.
Adding the multi-option field in the new block
Once we create the new featured_posts
block, we must create the field for storing the references with the posts.
)
Add the posts field into the new block
For adding the new field into the featured_posts
block, you can fill posts
as Name {1}, then click on the icon on the right of the name {2} for opening the popup to select the field type. In the field type list, select Multi-options {3}, and then click to Add button {4} to add the new field.
In this way, the new posts
field is listed in the block field list.
)
Select the new posts field
For editing the attributes of the new field, and setting the relations, select the posts
field {1} in the field list.
Selecting the field, you can access the field options where you can set the relation with other stories (for example, the posts).
)
Set the relations, selecting Stories as Source
To allow the selection of other stories, we will change the Source of the posts
field to Stories
{1}. Now, to limit the selection of the stories that could be added as relations, we can set the path folder where to retrieve the related stories.
)
Set the relations, selecting the path of related stories
Once you select Stories {1} as the source, you can now write the path posts/
{2} in the now available Path to folder of stories input located below the dropdown; however, this is optional. You can also choose to allow only specific content types via the Restrict to content type select box {3}.
The last step we need to take with the featured_posts
is to save it by pressing Save & Back to Fields button {4} at the top right corner.
)
The new featured posts block available in the Block Library
Now we have the block {1} in the Block Library, we can start using it in our stories in the content section.
Using the featured posts block in a story
To manage the stories, you have to go to the content section:
)
Using featured posts block
You can navigate back to the content section in the Storyblok space by clicking on Content {1} on the sidebar on the left side. Then, select the Home story {2} that should be available as Storyblok will create it as an example for new spaces. You can click the Create new button {3} in the top right corner if you do not have that entry. You won’t have to do that step if you already have the Home entry available.
Selecting the Home stories, you will access the Visual Editor.
)
Adding the new block to the Home story
By default, you will see the quickstart screen showing your draft token and additional useful information you can find here.
The next step we’re about to do is to use our new featured_posts
component. To do that, we will click the plus + button {1} in the component list on the right-side panel in the proper position we want to add the block.
Then, in the list of the nestable block, let’s click on Featured Posts {2} to add it to the content.
Once you click on the component you want to add Storyblok will add the instance to the current content and open that instance in the sidebar. You should now already be able to select the posts you want to feature. Let's feature the first post we created in the initial tutorial. This list will be empty if you do not have any entries in a folder with the slug posts so make sure to have a look at the first blog tutorial.
)
The Featured Posts block, added in the story
Once selected, we will have to save and/or publish our content entry to persist the changes and make them available in the Content Delivery API. Pressing the Publish button {1} you will save and publish the content.
Clicking the Save button {2} you will save the content in draft
version.
The next thing to do is to retrieve the content from our API, so you can use it in your application with your preferred technology. We can do this in two ways, namely:
- We can make an API call to fetch the
home
story and make another API call to retrieve thefeatured_posts
using theirUUIDs
. - We can add a query parameter to our API request stating the relationship we want to resolve. With this approach, we won't have to make two API requests.
Retrieving the story content via API
You can either look at our Content Delivery API and learn how to retrieve one story from there, or you can press the little arrow right next to the Publish button to access the Draft JSON (Api V2) option. Storyblok will open a new tab with the GET
request that you can use to access your content.
A demo link can be found here.
)
Home stories draft JSON
We can now follow two approaches to retrieve the post behind that uuid
. We can either choose to send another request and retrieve multiple stories by uuids or we can use the resolve_relations
query param that Storyblok has available.
Resolving the relationship via the Content Delivery API
Keep in mind that the resolved relationship will not be included in the content of the home
story but under the rels
array below the story
when using an API request. That said, it will also not be reflected in the input
Storyblok JS Bridge as it is an API operation and changes the default structure, so disabling the input or check for your references being uuid
strings or objects need to be done.
The resolve_relations
parameter is a way the API allows you to define a list of component_name.field_name
pairs that it should try to resolve. Resolving relationship will work with arrays of uuids
(as we have from the multi-options field type) or with fields that contain one uuid
as a string (the single-option field type for example). So what do we have to pass to the resolve_relation parameter?
Let’s have a look at our data structure: the field that contains the array of uuids
is called posts and the component that fields belong to is called featured_posts
so we end up with a parameter like resolve_relations=featured_posts.posts
. Let’s add that to the end of our request and see how the response will change.
The URL should look like this now:
https://api.storyblok.com/v2/cdn/stories/home?version=draft&token=iZQea1zPq7vfywVh8tdRxQtt&cv=1642873857&resolve_relations=featured_posts.posts
)
Featured posts JSON data
We can see that we now have the whole object of the referenced content available. That allows us to directly access the fields of that referenced content entry, without having to do another request.
If you use the storyblok-js-client
and storyblok-bridge
the resolved relations (in this case featured_posts
) will be added to the original place of uuids
as well as in rels
but when you use an API request, they are only added to the rels
array.
)
Resolve relations JSON
Reducing the size of the response
Resolve relations allow the information about the stories involved in the relationship to be obtained in a single HTTP call. This has an impact on the size of the response. In case you want to reduce the size of the response at the cost of making two HTTP calls, however, you can proceed as follows:
- Make the first call to get the main history without relationship resolution;
- Retrieve the list of UUIDs of related stories in the body of the response
- make a second HTTP call to get a list of stories (learn how to retrieve multiple stories in our docs) based on the UUIDs, using either the
by_uuids
parameter (in case the order of related stories is not important) or theby_uuids_ordered
parameter (in case the order is important).
For the HTTP call to retrieve multiple stories you can specify the excluding_fields
parameter for excluding specific fields of your content type by comma-separated names. This can significantly reduce the size of your API response. Example: excluding_fields=title,content
.
Resolving the relationship via GraphQL
If you use the Storyblok GraphQL endpoint in your app, you can also resolve the relationship between content entries using the story id and specifying the relationship, as seen below.
{
PageItem(id: {STORY_ID}, resolve_relations: {RELATIONSHIP}) {
id
content {
body
component
}
}
}
Example:
{
PageItem(id: "106765222", resolve_relations: "teaser-first-lvl.rel,teaser-second-lvl.rel") {
id
content {
body
component
}
}
}
If you want to explore the Storyblok GraphQL functionalities, you can use the playground at this URL: https://gapi-browser.storyblok.com/?token=insert-here-your-access-token
The result can be seen below.
)
Storyblok, resolving relations via GraphQL query
Resolving relations using the JavaScript Client
In the previous sections, we explored how the APIs (Restful and GraphQL) work. To simplify the process, you could use the Universal JavaScript Client for Storyblok's API.
You can retrieve the story and related stories via JavaScript code. To do this, let's see how:
- install the Client
- correctly initialize the library
- retrieve the content
- parse the content
To do all the steps above, we will use the Universal JavaScript Client. If you're using a specific framework like Next.js/React or Nuxt/Vue or Sveltekit/Svelte or whatever, you'll need to refer to the library-specific SDK. Still, the retrieval and initialization logic is the same.
Install the Client
To install the Client, you can use npm
or yarn
:
npm install storyblok-js-client
Once you have installed the Client, you can start using it.
Initialize the Storyblok Client
Creating a new file or, if you are using a framework, in the proper place for loading external data, you can initialize the library. You have to import the StoryblockClient
, and you have to instance it using a proper token.
import StoryblokClient from "storyblok-js-client";
const Storyblok = new StoryblokClient({
accessToken: 'your-access-token',
});
Then, once you have your Storyblok
object instance, you can perform a get on the home story.
Retrieving the content via get() method
Performing a get()
, you have to send also:
- the
version
parameter: set as "draft
" if you want draft content or "published
" if you want to retrieve published data. Remember to use the proper token in theStoryblokClient
initialization (we have aPreview
token that allows you to retrieve draft and published content, and we havePublish
token for retrieving only published content) - the
resolve_relations
option: set with the name of the block name (featured_posts
) and the field name (posts
), for examplefeatured_posts.posts
.
let response = await Storyblok.get('cdn/stories/home', {
version: 'draft',
+ resolve_relations: 'featured_posts.posts',
})
Once you have the response, you can start to walk through the structure.
Parsing the content
Considering that to access the story, you have to use the nested object: response.data.story,
and because of the content, once you have your story, you can access to content.body
with the list of the block included in your story. One of these blocks is the "featured_posts" block, and you can retrieve that via this example code (obviously, you have to adapt it based on your needs, in this case, we are just performing a console.log()
:
// accessing to the response.data.story
let home = response.data.story
// walking through the content body
home.content.body.forEach(element => {
if (element.component === 'featured_posts') {
// once we are on the right block, we can access to the block content via posts
console.log(element.posts)
}
});
Once you access the right block and accessing to the posts (the name of the field used as a multi-option in the block), you can retrieve the list of the related stories.
Parsing the object that comes from the SDK is easier than using the API directly. Because the APIs structure exposes the relations in a specific section in the JSON response (the rels
section). Once it retrieves the story via API, the SDK re-arranges the structure of the response in an object where your relations are nested directly in the parent object.
To allow the JS Client to copy the related story from rels
to the right place in the root story
object, you must set the field name (for example, posts
) with the component's name (for example, featured_posts
) as a prefix, like featured_posts.posts
.
Using the Storyblok Bridge
If you are using Storyblok Bridge, in addition to configuring relationships for API calls, you will need to configure relationship resolution during Storyblok Bridge initialization.
For example, if your relationship in the example above is featured_posts.posts
, you need to initialize the Storyblok Brige using the resolveRelations
option. Pay attention to the fact that for API calls the option is resolve_relations
(with the underscore) while the initialization of the Storyblok Bridge uses the camel case notation. So for setting up correctly the Storyblok Bridge:
useStoryblokBridge(story.id, (story) => (state.story = story), {
+ resolveRelations: ["featured_posts.posts"],
});
Resolving more than 50 relations
If the total number of relationships to be resolved is greater than 50, for performance reasons, the API service response will not contain the related stories in the rels
attribute. Instead, the list of identifiers ( uuids
) of the stories that should be resolved is returned. This array of UUIDs is included in the rel_uuids
attribute.
The expansion of the resolved stories is then delegated to the client using the list of identifiers included in the rel_uuids
attribute (an array of strings). Suppose you're using one of the SDKs provided by Storyblok under the hood when the response is received; in that case, additional API calls are made by the SDK (to the stories
endpoint) based on the array of identifiers to retrieve related stories. The SDK then injects the stories into the correct place in the main story structure.
A practical example for retrieving stories via the Storyblok JS Client package:
// Importing Storyblok client import StoryblokClient from "storyblok-js-client"; // Initializing the Storyblok client const Storyblok = new StoryblokClient({ accessToken: "youraccesstoken", apiOptions: { cache: { type: 'none' }, timeout: 5 }, }); // Retrieving the Story with slug `home` and resolving the relation for the content type `Page` in the field `related_stories` await Storyblok.get('cdn/stories/home', { version: 'draft', resolve_relations: 'Page.related_stories', }) .then((response) => { // the story console.log(response.data.story) // the related stories for the `home` story console.log(response.data.story.content.related_stories) // the list of uuids of related stories console.log(response.data.rel_uuids) // If the number of the resolved stories is greater than 50 we can access to rel_uuids // for retrieving the list of ids instead of // having rels as list of story objects }) .catch((error) => { console.log(error) })
Using specific framework SDK
If you are using some specific SDK for Nuxt or Next.js, or SvelteKit, for instancing the Storyblok objects and to get the content, I will share the basic tutorials with you to make a proper connection. The important thing to consider is to use the resolve_relations
parameter when you fetch the content.
The tutorial specific to the frameworks:
- Connect SvelteKit with Storyblok;
- Connect Next.js with Storyblok;
- Connect Nuxt with Storyblok.
In the case of framework like Nuxt, Next,js or SveltKit, for example, you can use a helper (provided by the framework-specific SDKs) that wraps the Storyblok.get()
method, but the important thing to remember for retrieving the relations, is to use the right option parameter resolve_relations
:
/**
* Resolve relations
*/
let resolveRelations = [
'banner-reference.banner',
'featured-articles-section.articles',
'article-page.categories',
'article-page.author',
]
const storyblokApi = useStoryblokApi()
const { data } = await storyblokApi.get('cdn/stories/' + slug, {
version: 'draft',
resolve_relations: resolveRelations
})
Then it would help if you used the same resolveRelations
array in the Storyblok Bridge initialization. For example, in on the mount of your page component, you can use something like this:
useStoryblokBridge(story.value.id, (evStory) => (story.value = evStory), {
+ resolveRelations: resolveRelations,
})
Wrapping Up
With the resolve_relations
parameter of Storyblok, you can easily include referenced resources to save API requests, even though we do not charge extra for your API requests this is a quick and easy to use way to save you some time in development. You can use it for featured posts, resolving authors and categories on the post detail page. Maybe reference related or similar products on a product details page without performing another request.