Open navigation

Integration Steps

Contentful is an API-first content platform that helps you manage content, orchestrate experiences, and deliver on any digital touchpoint. In this guide, we’ll take you through the steps to generate a feed of your Contentful content so that this can be indexed to Klevu.

By following this guide we have created a sample template you can use to create feed(s) according to Klevu’s format. In this example, we’ve generated a feed to index CMS/blog content, but the same approach can be tailored by you to add your product catalogue and/or any other content.

For this example, we created a new Contentful account and used the Deploy an example approach presented on the Contentful account dashboard to get started. This example requires using Netlify and its serverless functions functionality. We deployed a Gatsby blog to Netlify by clicking Deploy with Netlify.

Please note, We assume your function folder is functions (default folder), if it's not then adapt the folder structure accordingly below.

Step-by-step instructions

  1. Create a folder called functions in your root
  2. Create a folder called rss inside the functions folder
  3. Create a file called rss.js inside this structure, so the path becomes <root>/functions/rss/rss.js
  4. Paste in the content below as a template to start customising
    const https = require("https");
    
    function addLeadingZero(num) {
      num = num.toString();
      while (num.length < 2) num = "0" + num;
      return num;
    }
    
    // See https://github.com/whitep4nth3r/rfc-822
    // and https://whitep4nth3r.com/blog/how-to-format-dates-for-rss-feeds-rfc-822/
    
    function buildRFC822Date(dateString) {
      const dayStrings = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
      const monthStrings = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
    
      const timeStamp = Date.parse(dateString);
      const date = new Date(timeStamp);
    
      const day = dayStrings[date.getDay()];
      const dayNumber = addLeadingZero(date.getDate());
      const month = monthStrings[date.getMonth()];
      const year = date.getFullYear();
      const time = `${addLeadingZero(date.getHours())}:${addLeadingZero(date.getMinutes())}:00`;
      const timezone = date.getTimezoneOffset() === 0 ? "GMT" : "BST";
    
      //Wed, 02 Oct 2002 13:00:00 GMT
      return `${day}, ${dayNumber} ${month} ${year} ${time} ${timezone}`;
    }
    
    async function getPosts() {
      return new Promise((resolve, reject) => {
        const query = `
        query {
          blogPostCollection {
            items {
              sys {
                firstPublishedAt
                id
              }
              title
              shortDescription
              description {
                json
              }
              slug
            }
          }
        }
        `;
    
        const options = {
          protocol: "https:",
          hostname: "graphql.contentful.com",
          path: "/content/v1/spaces/8i2o0d5rt6lt",
          method: "POST",
          headers: {
            "Authorization": "Bearer VGGwjo1_NjJCjS3jhAfs0d-ocgF8J-Ze7HKEL2ZORyk",
            "Content-Type": "application/json",
          },
        };
    
        let posts = "";
    
        const req = https.request(options, (res) => {
          res.on("data", (data) => {
            posts += data;
          });
    
          res.on("end", () => {
            const parsedPosts = JSON.parse(posts);
            resolve(parsedPosts.data.blogPostCollection.items);
          });
        });
    
        req.on("error", (e) => {
          console.error(e);
        });
    
        req.write(JSON.stringify({ query }));
        req.end();
      });
    }
    
    function buildRssItems(items) {
      const truncateLength = 44;
    
      return items
        .map((item) => {
          const title = item.title;
          const description = item.description.json.content;
          const shortDescription = item.shortDescription;
          const link = item.slug;
          const id = item.sys.id;
          
          return `
            <item>
            <id>${id}</id>
            <item_type>KLEVU_CMS</item_type>
            <title>${title}</title>
            <description>${shortDescription}</description>
            <link>https://elaborate-pegasus-69af84.netlify.app/blog/${link}</link>
            <published_at>${buildRFC822Date(item.sys.firstPublishedAt)}</published_at>
            </item>
            `;
        })
        .join("");
    }
    
    exports.handler = async function (event, context) {
      const rssFeed = `<?xml version="1.0"?>
      <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
      <channel>
        <title>Klevu Contentful XML test</title>
        <atom:link href="https://elaborate-pegasus-69af84.netlify.app/.netlify/functions/rss" rel="self" type="application/rss+xml" />
        <link>https://klevu.com</link>
        <description>This is a sample feed for Contentful to send cms content to Klevu</description>
        ${buildRssItems(await getPosts())}
      </channel>
      </rss>`;
    
      return {
        statusCode: 200,
        contentType: "text/xml",
        body: rssFeed,
      };
    };
  5. In lines 54 and 57, change your space ID and API credentials.
  6. Starting at line 30, adjust the GraphQL query to pull the correct content types.
  7. Start line 87 by amending the variables and XML output based on your updated GraphQL query.
  8. Ensure the XML is matching Klevu’s format.
  9. The link will allow you to register a Klevu account if you haven't already done so.
  10. Send your feed to Klevu along with your API key and Klevu store.

Multilingual content

If you are using a field-level localisation approach, you can simply duplicate the above file and have one for each language. Then just amend the GraphQL query and the output to match the fields you want to put. This way you can create a feed for each language. This is possible for other localisation approaches too.

Did you find it helpful? Yes No

Send feedback
Sorry we couldn't be helpful. Help us improve this article with your feedback.