Parsing RSS feeds with serverless functions on the Hypi platform

Hypi’s lowcode features lets you get quite far in building web and mobile applications without writing any code but inevitably you’ll have some requirements that it doesn’t provide out of the box.
Two primary ways of getting those into your application is via an external service using the API Gateway or using serverless functions.

The docs already show how to create basic serverless functions but what’s some times not obvious is how you use dependencies in your serverless functions.
So in this post I want to demonstrate how you’d use NPM dependencies in a nodejs based serverless function. The process is similar for python, ruby and other languages Hypi supports.

The task is to parse an RSS feed (normally in XML format) and return the contents as JSON for your application to consume.
We’ll use the rsstojson package from NPM to do this.

Just as you would with any nodejs based project, we need a package.json to define our metadata including dependencies. Create a file called package.json:

{
  "name": "rsstojson",
  "version": "1.0.0",
  "main": "index.js",
  "dependencies": {
    "feedparser": "*",
    "request": "*"
  }
}

Now we need to install the dependencies which is done by running npm i. Once this completes you will have a folder called node_modules.
Now we’re ready to use our dependency so let’s write our function’s code - create a file index.js and add this to it:

var FeedParser = require('feedparser');
var request = require('request');

function main(args) {

	return new Promise(function(resolve, reject) {

		var fp = new FeedParser();
		
		request.get(args.url).pipe(fp);

		var items = [];
		var meta;

		fp.on('readable', function() {
			var stream = this;
			var item;

			while(item = stream.read()) {
				if(!meta) meta = item.meta;
				delete item.meta;
				items.push(item);
			}
		});

		fp.on('end', function() {
			resolve({items:items, meta:meta});
		});

	});

}

exports.main = main;

The name index.js is significant - Hypi’s serverless is powered by OpenWhisk and packaged actions (those with dependencies like this) must have an index.js file at the root.

Alright, now we need to create the package that we’ll deploy to Hypi. We do this by adding the index.js and node_modules folder to a zip file. You can do this with any archive program or from the command line run the following:

zip -r action.zip index.js node_modules

Here, we’re adding index.js and node_modules into a file called action.zip.

We can now create our function in Hypi by running:

wsk action create feedparser --kind nodejs:12 action.zip

This assumes you’ve installed and configured the wsk CLI to use Hypi.

Once completed we can test the function using:

wsk action invoke --result feedparser --param url https://hypi.io/feed/

This will produce a JSON response containing the feed from Hypi’s blog.
Now you can use this function in your Hypi app by referencing the name feedparser for example

type Query {
    parseFeed(url: String): Json @tan(type: OpenWhisk, name: "feedparser")
}        

This enables you to call parseFeed like any other function in your Hypi API.

{
  parseFeed(url: "https://hypi.io/feed")
}

This post’s example is an adoption from this blog post.

Output from the test (trimmed)

{
  "items": [
    {
      "author": "asasamp",
      "categories": [
        "hypi",
        "tutorials",
        "API gateway of Hypi",
        "http directive",
        "Hypi's API gateway",
        "web push notifications"
      ],
      "comments": "https://hypi.io/2021/06/17/web-push-notifications-using-api-gateway-of-hypi/#respond",
      "date": "2021-06-17T07:00:00.000Z",
      "dc:creator": {
        "#": "asasamp",
        "@": {}
      },
      "description": "How to send web push notification using Hypi's API gateway? Check out the solution in this tutorial!",
      "enclosures": [],
      "guid": "https://hypi.io/?p=5399",
      "image": {},
      "link": "https://hypi.io/2021/06/17/web-push-notifications-using-api-gateway-of-hypi/",
      "origlink": null,
      "pubDate": "2021-06-17T07:00:00.000Z",
      "pubdate": "2021-06-17T07:00:00.000Z",
      "rss:@": {},
      "rss:category": [
        {
          "#": "hypi",
          "@": {}
        },
        {
          "#": "tutorials",
          "@": {}
        },
        {
          "#": "API gateway of Hypi",
          "@": {}
        },
        {
          "#": "http directive",
          "@": {}
        },
        {
          "#": "Hypi's API gateway",
          "@": {}
        },
        {
          "#": "web push notifications",
          "@": {}
        }
      ],
      "rss:comments": {
        "#": "https://hypi.io/2021/06/17/web-push-notifications-using-api-gateway-of-hypi/#respond",
        "@": {}
      },
      "rss:description": {
        "#": "How to send web push notification using Hypi's API gateway? Check out the solution in this tutorial!",
        "@": {}
      },
      "rss:guid": {
        "#": "https://hypi.io/?p=5399",
        "@": {
          "ispermalink": "false"
        }
      },
      "rss:link": {
        "#": "https://hypi.io/2021/06/17/web-push-notifications-using-api-gateway-of-hypi/",
        "@": {}
      },
      "rss:post-id": {
        "#": "5399",
        "@": {
          "xmlns": "com-wordpress:feed-additions:1"
        }
      },
      "rss:pubdate": {
        "#": "Thu, 17 Jun 2021 07:00:00 +0000",
        "@": {}
      },
      "rss:title": {
        "#": "Web Push Notifications using API Gateway of Hypi",
        "@": {}
      },
      "slash:comments": {
        "#": "0",
        "@": {}
      },
      "source": {},
      "summary": "How to send web push notification using Hypi's API gateway? Check out the solution in this tutorial!",
      "title": "Web Push Notifications using API Gateway of Hypi",
      "wfw:commentrss": {
        "#": "https://hypi.io/2021/06/17/web-push-notifications-using-api-gateway-of-hypi/feed/",
        "@": {}
      }
    }
  ],
  "meta": {
    "#ns": [
      {
        "xmlns:content": "http://purl.org/rss/1.0/modules/content/"
      },
      {
        "xmlns:wfw": "http://wellformedweb.org/CommentAPI/"
      },
      {
        "xmlns:dc": "http://purl.org/dc/elements/1.1/"
      },
      {
        "xmlns:atom": "http://www.w3.org/2005/Atom"
      },
      {
        "xmlns:sy": "http://purl.org/rss/1.0/modules/syndication/"
      },
      {
        "xmlns:slash": "http://purl.org/rss/1.0/modules/slash/"
      },
      {
        "xmlns:georss": "http://www.georss.org/georss"
      },
      {
        "xmlns:geo": "http://www.w3.org/2003/01/geo/wgs84_pos#"
      },
      {
        "xmlns": "com-wordpress:feed-additions:1"
      }
    ],
    "#type": "rss",
    "#version": "2.0",
    "#xml": {
      "encoding": "UTF-8",
      "version": "1.0"
    },
    "@": [
      {
        "xmlns:content": "http://purl.org/rss/1.0/modules/content/"
      },
      {
        "xmlns:wfw": "http://wellformedweb.org/CommentAPI/"
      },
      {
        "xmlns:dc": "http://purl.org/dc/elements/1.1/"
      },
      {
        "xmlns:atom": "http://www.w3.org/2005/Atom"
      },
      {
        "xmlns:sy": "http://purl.org/rss/1.0/modules/syndication/"
      },
      {
        "xmlns:slash": "http://purl.org/rss/1.0/modules/slash/"
      },
      {
        "xmlns:georss": "http://www.georss.org/georss"
      },
      {
        "xmlns:geo": "http://www.w3.org/2003/01/geo/wgs84_pos#"
      }
    ],
    "atom:link": {
      "@": {
        "href": "https://hypi.io/feed/",
        "rel": "self",
        "type": "application/rss+xml"
      }
    },
    "author": null,
    "categories": [],
    "cloud": {},
    "copyright": null,
    "date": "2021-06-16T20:00:58.000Z",
    "description": "Lowcode serverless platform and service for mobile and web apps",
    "favicon": null,
    "generator": null,
    "image": {
      "title": "Hypi",
      "url": "https://hypi.io/wp-content/uploads/2021/01/favicon.ico"
    },
    "language": "en-GB",
    "link": "https://hypi.io/",
    "pubDate": "2021-06-16T20:00:58.000Z",
    "pubdate": "2021-06-16T20:00:58.000Z",
    "rss:@": {},
    "rss:description": {
      "#": "Lowcode serverless platform and service for mobile and web apps",
      "@": {}
    },
    "rss:image": {
      "@": {},
      "height": {
        "#": "32",
        "@": {}
      },
      "link": {
        "#": "https://hypi.io/",
        "@": {}
      },
      "title": {
        "#": "Hypi",
        "@": {}
      },
      "url": {
        "#": "https://hypi.io/wp-content/uploads/2021/01/favicon.ico",
        "@": {}
      },
      "width": {
        "#": "32",
        "@": {}
      }
    },
    "rss:language": {
      "#": "en-GB",
      "@": {}
    },
    "rss:lastbuilddate": {
      "#": "Wed, 16 Jun 2021 20:00:58 +0000",
      "@": {}
    },
    "rss:link": {
      "#": "https://hypi.io/",
      "@": {}
    },
    "rss:site": {
      "#": "179792237",
      "@": {
        "xmlns": "com-wordpress:feed-additions:1"
      }
    },
    "rss:title": {
      "#": "Hypi",
      "@": {}
    },
    "syn:updatefrequency": {
      "#": "1",
      "@": {}
    },
    "syn:updateperiod": {
      "#": "hourly",
      "@": {}
    },
    "title": "Hypi",
    "xmlUrl": "https://hypi.io/feed/",
    "xmlurl": "https://hypi.io/feed/"
  }
}