Generating a Custom RSS Feed with Cloudflare Workers: A Step-by-Step Guide
Tags: opensource, rss, cloudflare, typescript
Published: 2024-03-29 15:16:05


In the ever-evolving landscape of web development, the need for personalized content delivery mechanisms remains a constant. RSS feeds, a time-tested method for distributing content updates, can be tailored to deliver user-specific content, enhancing the user experience. This blog post outlines a practical approach to generating a custom RSS feed for the 20 latest shares of a user in [ChatCraft](https://github.com/tarasglek/chatcraft.org), leveraging Cloudflare Workers for a serverless solution. We'll also touch on adding CSS to our feed for improved styling.

Why Cloudflare Workers?
Cloudflare Workers provide a powerful, serverless execution environment that allows you to run code closer to your users, improving performance and reducing latency. This makes them an ideal choice for generating dynamic content like RSS feeds.

Step 1: Setting Up Your Cloudflare Worker
First, ensure you have a Cloudflare account and have set up Workers. You'll need to create a new Worker script, which will be the backbone of our RSS feed generator.

Step 2: Fetching the Latest Shares
Our goal is to generate an RSS feed for the 20 latest shares of a user. To achieve this, we'll list objects in a specific bucket (or directory) that corresponds to the user's shared content. Here's a simplified version of how you might fetch this data:

```
const { objects } = await CHATCRAFT_ORG_BUCKET.list({ prefix: `${user}/` });
  const sortedObjects = objects.sort((a, b) => b.uploaded.getTime() - a.uploaded.getTime());
  const recentObjects = sortedObjects.slice(0, 20);
```


Step 3: Parsing Content Without DOMParser
Since Cloudflare Workers do not support DOMParser, we'll use regular expressions to extract necessary information from the HTML content of each share. This includes the title, description, and any other metadata required for our RSS feed.

```
import { load } from "cheerio";
const chatContent = await chatData.text();
      const $ = load(chatContent);

      const title = $('meta[property="og:title"]').attr("content") || "No Title";
      const summary = $('meta[property="og:description"]').attr("content") || "No Summary";
      const url = $('meta[property="og:url"]').attr("content") || "No URL";
      const id = url.split("/").pop() || "No ID";
      const preContent = $("pre").text();
      const dateMatch = preContent.match(/date:\s*(.+)/i);
      const date = dateMatch ? new Date(dateMatch[1]) : new Date();
```

Step 4: Generating the RSS Feed
With the data extracted, we can now populate our RSS feed. We'll use a library like Feed to create the feed, adding each share as an item:

```
const feed = new Feed({
  title: `User Feed for ${user}`,
  // Other feed properties...
});

// Add items to the feed
objects.forEach(object => {
  // Extract and add data for each object
  feed.addItem({
    title: title,
    // Other item properties...
  });
});
```

Step 5: Adding CSS to the Feed
To add CSS to our RSS feed, we'll include a link to an XSL stylesheet in our XML output. This can enhance the presentation of the feed when viewed directly in a browser. Also feed doesn't support add css so I add it by myself and delete the second XML declaration.

```
  // Remove the first line (second XML declaration) if it exists
  const lines = feedXml.split("\n");
  if (lines[0].startsWith("<?xml")) {
    lines.shift();
    feedXml = lines.join("\n");
  }

  feedXml =
    `<?xml version="1.0" encoding="UTF-8"?>\n<?xml-stylesheet type="text/xsl" href="${xsltUrl}"?>\n` +
    feedXml;
```
Ensure you have an XSL stylesheet (style.xsl) hosted and accessible at the specified URL.

Step 6: Deploying the Feed
Finally, we'll store our generated RSS feed in the Cloudflare Worker's storage, making it accessible to users:

```
await CHATCRAFT_ORG_BUCKET.put(feedKey, new TextEncoder().encode(feedXml), {
  httpMetadata: {
    contentType: "application/atom+xml",
  },
});
```

Conclusion
By leveraging Cloudflare Workers, we've created a dynamic, user-specific RSS feed that fetches the 20 latest shares, parses content without DOMParser, and includes custom CSS for styling. This approach demonstrates the flexibility and power of serverless functions for personalized content delivery. Whether you're a blogger, developer, or content creator, this method opens up new possibilities for engaging your audience with tailored feeds.

Remember, while regular expressions are a quick solution for parsing HTML content, they may not be foolproof for complex HTML structures. Always test thoroughly and consider alternative parsing methods if your use case requires it.