⚠️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

Generating an RSS Feed with a Headless CMS Using Storyblok APIs

Try Storyblok

Storyblok is the first headless CMS that works for developers & marketers alike.

In the digital age, staying up-to-date with the vast amount of web content can be overwhelming. However, generating an RSS feed for your website can revolutionize the way you connect with information. In this article, we explore how to generate an RSS feed with a headless CMS providing examples and some hints.

RSS feeds simplify content distribution by consolidating updates from multiple sources, allowing users to stay updated with ease. They also offer personalized content aggregation, enhance accessibility for offline reading, and provide valuable insights for niche markets and research. Harnessing the power of RSS feeds empowers both content providers and consumers in the digital world.

In this tutorial, we will demonstrate how to use the Storyblok APIs to create an RSS feed specifically for articles/news content types (though it can also be adapted for other content entries). Our implementation will use JavaScript to fetch and display the content on a website, providing you with a clear understanding of the process.

Using Content Delivery API and the Storyblok JS client

We will use the Storyblok Content Delivery API to load our content entries via the stories endpoint.

If you plan to use JavaScript for accessing the APIs, I suggest using Storyblok JS client.

For retrieving data from the Storyblok Content Delivery API, you need to get the access token from your Storyblok Space.

Installing the Storyblok JS client

To access the API via the Storyblok JS client in your JavaScript code, you need to install the storyblok-js-client library. So in your project directory, where you have your package.json file, launch the npm command with the install option:

        
      npm install storyblok-js-client --save
    

Once you installed the package, you can start using it in your JavaScript code.

Getting the content

In the next snippet, we are going to import the package, then create the instance of StoryblokClient using the proper access token, and finally retrieve all the published stories.

        
      // 001 importing the storyblok-js-client
import StoryblokClient from "storyblok-js-client";

// 002 Creating and setting up the StoryblokClient
const Storyblok = new StoryblokClient({
  accessToken: "your-access-token",
});

// 003 Retrieving the stories
let stories = await Storyblok.getAll("cdn/stories", {
  version: "published",
});
console.log(stories); // the array of stories, becuase you are using getAll()
    

In the stories variable, we have an array of Story objects. If you want to know more about the Story object structure, you can read more in the Content Delivery APIs documentation.

Typically the response of a list of objects in Storyblok APIs is paginated, which means that only a certain amount of entries are returned by the API call. Then if you want to retrieve all the entries, you have to manage the pagination mechanism and perform additional API calls to retrieve all the pages.

Thanks to the Storyblok JS client, you have the getAll() method that, under the hood, performs multiple API requests for retrieving all the pages if the amount of entries requested is greater than the entries of a single page.

Retrieving stories for the feed

You can adjust the parameters while performing the API call to retrieve the proper stories you want to share via the RSS feed. The second argument of the getAll() method defines the parameters that determine the retrieved stories. For example, if the stories used for the RSS field are organized in a specific folder (for example, the articles folder), and the stories are created with a specific content type (for example, the article-page content type), you can use the respectively the starts_with parameter and the content_type parameter:

        
      // 003 Retrieving the stories
let stories = await Storyblok.getAll("cdn/stories", {
  version: "published", // the published stories
  starts_with: "articles", // from the `articles` folder
  content_type: "article-page", // only  `article-page` content type
});
console.log(stories); // the array of stories
    

So in the example, we are using:

  • version parameter for retrieving only published stories;
  • starts_with parameter for retrieving stories from a specific folder;
  • content_type parameter for retrieving a specific type of story (based on a specific content type)

Sorting the stories

To control the sorting of the stories you want to share via the RSS feed, you can use the sort_by parameter. For example, for the RSS feed you are generating, you want to sort the stories by the first published date in descending mode (the most recent first). In this case, you can use the sort_by parameter in the parameter list:

        
      let stories = await Storyblok.getAll("cdn/stories", {
  version: "published",
  starts_with: "articles", // from the `articles` folder
  content_type: "article-page", // only `article-page` content type
  excluding_fields: "categories,image,text,content",
  sort_by: "first_published_at:desc",
});
console.log(stories); // the array of stories
    

As you can see, the value of the sort_by parameter is in the form field_name:asc (for ascending sorting) or field_name:desc (for descending sorting). In the example, you can use sort_by: "first_published_at:desc". For adding more examples, if you want to order the stories by position you can use sort_by: "position:asc". The position field reflects the position defined by the content editors in the Storyblok UI. The content editor can arrange the sorting via drag 'n drop the stories in the Content section. If you want to sort the stories by specific content, for example, the headline of the stories (and you have the headline field in the content type), you can use the prefix content. So in the end: sort_by: "content.headline:asc".

Limiting the number of stories

With the getAll() method, you can retrieve all the stories through the pagination solved directly by the package.

If, in your RSS feed, you want to share the latest ten stories, you can use the per_page parameter. So, in this case, you can use the get() method instead of the getAll() method and use per_page set to the value 10. Typically when you use the per_page parameter, you can also use the page parameter to define which page you want, but in this specific case we can avoid setting the page parameter because if the page parameter is omitted, the default is 1.

        
      let responseStories = await Storyblok.get("cdn/stories", {
  version: "published",
  starts_with: "articles", // from the `articles` folder
  content_type: "article-page", // only `article-page` content type
  sort_by: "first_published_at:desc", // sorting by last published date
  per_page: 10, // retrieving only 10 stories
});

console.log(responseStories.data.stories);
responseStories.data.stories.forEach((item) => {
  console.log(item.name);
  console.log(item.first_published_at);
});
    

One of the most important differences between the getAll() and get() methods is that the getAll() returns the array of stories. The get() method returns the response (with the body with the stories, with the headers etc). So, if you are using the get() method, for accessing the array of stories you need to access to stories attribute in the data attribute, so responseStories.data.stories.

Generating the RSS feed

Once you have retrieved the stories to share via the RSS feed, you can transform the array of stories into the proper format required for the RSS feed. If you want to explore more about the RSS format and how to create the RSS feed with proper syntax and format you can take a look at the RSS specification.

If you retrieved the responseStories object from the get() method you can:

  • walking through the responseStories.data.stories via map function setting initializing the story object for each array item;
  • for each story, generating the proper XML tags and accessing the story content via story.content;
  • formatting the date for the pubDate tag in the RFC 822 format using toUTCString() method.
        
      const hostname = "http://www.example.com";
const rssTitle = `RSS Feed for ${hostname}`;
const rssDescription = `This is the RSS Feed for the ${hostname} website`;

let rss_entries = responseStories.data.stories.map((story) => {
  return `\n<item>
  <title>${story.content.headline}</title>
  <description>${story.content.subheadline}</description>
  <link>${hostname}/${story.full_slug}</link>
  <guid isPermaLink="false">${story.uuid}</guid>
  <pubDate>${new Date(story.published_at).toUTCString()}</pubDate>
 </item>`;
});

let rss = `<?xml version="1.0" encoding="UTF-8" ?>
  <rss
      version="2.0"
      xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
   <title>${rssTitle}</title>
   <description>${rssDescription}</description>
   <link>${hostname}</link>
   <atom:link
       href="${hostname}/rss.xml"
       rel="self" type="application/rss+xml" />
   <ttl>1800</ttl>

   ${rss_entries.join("")}

  </channel>
  </rss>`;

console.log(rss);
    

In the example, we are generating the string with the XML for the RSS feed. Then you can decide to send the string as a response for replying to the request or generate the rss.xml static file to be deployed on your web service.

Summary

You’ve now learned how to access the stories API and use its content to generate an RSS feed. You can use the same approach to generate your own RSS feed with another technology like PHP, Python or any other. A similar setup is also used to build a sitemap, instead of the stories endpoint, it will use the links endpoint tho since you won't need the entries content for a sitemap. RSS feeds are capable of using the Authors and Categories tags if you want to add them to your RSS feed you can check out the tutorials in the table below to learn how to resolve the relationships between content entries.

ResourceLink
Setting Up the Blog Content Structure in Storyblokhttps://www.storyblok.com/tp/setting-up-blog-content-structure
How to create a post <> author relationshiphttps://www.storyblok.com/tp/how-to-build-relationships-between-2-content-types
How to create a post <> categories relationshiphttps://www.storyblok.com/tp/how-to-build-a-content-relationship
How to generate a sitemap with a Headless CMS using Storyblok APIshttps://www.storyblok.com/tp/how-to-generate-sitemap-headless-cms
Storyblok Content Delivery API, the "stories" endpointhttps://www.storyblok.com/docs/api/content-delivery/v2#core-resources/stories/stories
How to retrieve multiple stories using Storyblok Content Delivery APIhttps://www.storyblok.com/docs/api/content-delivery/v2#core-resources/stories/retrieve-multiple-stories
Storyblok Content Delivery API, the "links" endpointhttps://www.storyblok.com/docs/api/content-delivery/v2#core-resources/links/links

Authors

Dominik Angerer

Dominik Angerer

A web performance specialist and perfectionist. After working for big agencies as a full stack developer he founded Storyblok. He is also an active contributor to the open source community and one of the organizers of Scriptconf and Stahlstadt.js.

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.