⚠️Draft Content

Storyblok Raises $80M Series C - Read News

What’s the True Total Price of Enterprise CMS? Find out here.

Skip to main content

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 story that you receive by creating a new space already includes three components: Teaser, Grid, and Feature components. 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:

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 containing our articles' information.

We must jump into the Block Library section to create the featured post block.

Block library
1
2

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
1
2
3

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
1
2
3
4

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
1

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
1

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.

hint:

In this case, you can choose the Global Reference Field as this field-type already locks the Source to Stories by default. For content editors, using Global Reference Field might be straightforward.


Set the relations, selecting the path of related stories
1
2
3
4

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 the folder of stories input located below the dropdown; however, this is optional. You can also 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} in the bottom right corner.

The new featured posts block available in the Block Library
1

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:

Find Home story
1
2
3

Find Home story

You can navigate back to the content section in the Storyblok space by clicking on Content {1} on the left sidebar. Then, select the Home story {2} that should be available, as Storyblok will create it as an example for new spaces. If you do not have that entry, you can click the Create new button {3} in the top right corner. You won’t have to do that step if you already have the Home story available.

Selecting the Home stories, you will access the Visual Editor.

Adding the new block to our Home story
1
2

Adding the new block to the Home story

By default, you will see the quickstart screen showing your draft token, and you can find additional useful information 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 blocks, 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
1
2

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 the featured_posts​ using their UUIDs.
  • 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.

EXAMPLE:

An example URL to the draft JSON can be found here.


Home stories, draft JSON

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

​​Remember that the resolved relationship will not be included in the story's content but under the rels​ array below 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 checking for your references being uuid​ strings or objects needs to be done.

The resolve_relations parameter is how the API allows you to define a list of component_name.field_name pairs it should try to resolve. Resolving relationships 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 the parameter will be 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

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.

IMPORTANT:

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. However, when you use an API request, it will be only added to the rels array.

Resolve relations JSON

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 a 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 the by_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. An example: excluding_fields=title,content .


Resolving the relationship via GraphQL

Using 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
    }
  }
}
    
hint:

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

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. (storyblok-js-client)

You can retrieve the story and related stories via JavaScript code. To do this, let's see how:

  1. install the Client
  2. correctly initialize the library
  3. retrieve the content
  4. 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:

Installing the Javascript SDK
        
      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 API key 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 the StoryblokClient initialization (we have a Preview token that allows you to retrieve draft and published content, and we have Publish 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 example, featured_posts.posts.
Getting content with relations
        
      let response = await Storyblok.get('cdn/stories/home', {
    version: 'draft',
+    resolve_relations: 'featured_posts.posts',
})
    

Once you have the response, you can 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 blocks 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():

Parsing the content and the relations
        
      // 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 access 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 API 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 response structure in an object where your relations are nested directly in the parent object.

hint:

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 to set up correctly the Storyblok Bridge:

Configure the Storyblok Bridge with resolve relations
        
      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, the API service response will not contain the related stories in the attribute for performance reasons. 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 use some specific SDK for Nuxt Next.js or SvelteKit to start the Storyblok objects and 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:

In the case of frameworks 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:

Fetching content using useStoryblokApi() helper from frameworks SDK
        
      /**
 * 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, on the mount of your page component, you can use something like this:

Setup the Storyblok Bridge for resolving relations
        
        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.

Author

Roberto Butti

Roberto Butti

Roberto is a Developer Relations Engineer at Storyblok who loves supporting teams for building websites focused on the code's performance and code quality. His favorite technologies include Laravel, PHP, Vue, Svelte and ... Storyblok. He loves exploring and designing composable architectures.