Martin Helmut Fieber

Feed at its best

Posted on

RSS, Atom, feed?

When it comes to web syndication, many names are floating around, some used synonymously. RSS is often seen as the name for the functionality of a feed itself, but it is not, it is rather one way of delivering website content in a standardised way. Another one is Atom, going a bit further, being a standard for a feed format and publishing protocol, seen as an alternative to RSS.

Both do the same, in slightly different ways. Without going into too much detail, Atom gives more flexibility and is an excellent web standard; the format I added to my site.

Awesome feed

Going under the radar of many, website feeds are amazing for a lot of reasons. For me, it is being able to subscribe to the exact content I like. Everything from art, games and game development, programming, books, … things I actually want to read.

If a website serves full content via the feed I can even read everything inside my reader app*. Other sites, delivering only headlines and/or summaries, create a content stream for me that I control and can dive deeper into, if I so desire.

The only thing I would wish for is that those feeds were more easily explorable.

Feed visibility, feed availability

I find myself quite often wondering if a website, blog, or portfolio has a feed. Not always is there a link, icon, or reference mentioned. Besides not being visible, many do not offer any kind of feed; still, there are way more sites out there that do.

If I am not sure, I use my reader to find out — I paste the link into it and if a feed can be discovered it will tell me. This works surprisingly more often than not. The way this "auto discovery" is done is through a meta-tag in the websites head, linking to the feeds XML file.

<link
  rel="alternate"
  type="application/atom+xml"
  title="My Blog"
  href="/feed.xml"
/>
This is the HTML tag for an Atom feed. For RSS the type needs to be application/rss+xml.

My blog as feed

As I love using feeds, I for sure needed to serve my blog as feed as well. The base XML file structure for the Atom feed will look like the following.

<?xml version="1.0" encoding="utf-8" ?>
  <feed
    xmlns="http://www.w3.org/2005/Atom"
    xml:base="absolute/url/to/blog">
  <title>Martin Fieber's Blog</title>
  <subtitle>
    Some extra information ...
  </subtitle>
  <link
    href="absolute/url/to/feed.xml"
    rel="self" />
  <link href="absolute/url/to/blog" />
  <icon>absolute/url/to/favicon.ico</icon>
  <logo>absolute/url/to/logo.png</logo>
  <updated>2022-11-05T10:10:10Z</updated>
  <id>absolute/url/to/blog/</id>
  <author>
    <name>Martin Helmut Fieber</name>
    <email>info@martin-fieber.se</email>
  </author>
  <entry>
    <title>Post title</title>
    <link href="absolute/url/to/post" />
    <updated>2022-10-20T00:00:00Z</updated>
    <id>absolute/url/to/post</id>
    <summary xml:lang="en" type="html">
      Content summary
    </summary>
    <content xml:lang="en" type="html">
      Full content
    </content>
  </entry>
  <!-- more entries ... -->
</feed>

The first tags are setting up the details of the feed connected to my blog; title and subtitle, a link to the feed and the blog, icons, last update date, unique identifier, and information about me as author of the blog.

A post is defined through the contents of the entry tag, the base being the title of the post, a link, update date, and unique identifier. The summary and content tags are respectively for a short post summary and the full content of the post — there can be both, one, or none.

I add both, as I want people to be able to read the whole post from any reader app.

As I use Eleventy I will take advantage of it and generate the feed file from my posts, using the Eleventy RSS plugin. For this I set up a feed.njk file, using the Nunjucks template language, with some data defining the target file name feed.xml as well as my site and blog url.

<!-- /feed.xml -->

---json
{
  "permalink": "feed.xml",
  "eleventyExcludeFromCollections": true,
  "metadata": {
    "blogUrl": "https://martin-fieber.de/blog/",
    "siteUrl": "https://martin-fieber.de/"
  }
}
---
<?xml version="1.0" encoding="utf-8"?>
<feed
    xmlns="http://www.w3.org/2005/Atom"
    xml:base="{{ metadata.blogUrl }}">
  <!-- ... -->
</feed>

The first section separated by --- is front matter data as JSON. Inside the feed tags comes the feed title, author, and some more.

  <title>Martin Fieber's Blog</title>
  <subtitle>...</subtitle>
  <link
    href="{{
      permalink
        | absoluteUrl(metadata.siteUrl)
    }}"
    rel="self"/>
  <link href="{{ metadata.blogUrl }}"/>
  <icon>
    {{ metadata.siteUrl }}favicon.ico
  </icon>
  <logo>
    {{ metadata.siteUrl }}assets/favicon/icon-192.png
  </logo>
  <updated>
    {{
      collections.posts
        | getNewestCollectionItemDate
        | dateToRfc3339
    }}
  </updated>
  <id>{{ metadata.blogUrl }}</id>
  <author>
    <name>Martin Helmut Fieber</name>
    <email>info@martin-fieber.se</email>
  </author>

To generate the entries for my post, I want to have the latest post on top, so I need to reverse the order of my post collection. I also use absolute URLs everywhere.

  {%-
    for post in collections.post
      | reverse
  %}
  {%-
    set absolutePostUrl = post.url
      | absoluteUrl(metadata.blogUrl)
  %}
  <entry>
    <title>{{ post.data.title }}</title>
    <link href="{{ absolutePostUrl }}"/>
    <published>
      {{ post.date | dateToRfc3339 }}
    </published>
    <updated>
      {{
        post.data.lastmod | dateToRfc3339
          if post.data.lastmod
          else post.date | dateToRfc3339
      }}
    </updated>
    <id>{{ absolutePostUrl }}</id>
    <summary xml:lang="en" type="html">
      {{
        post.data.summary
          | htmlToAbsoluteUrls(absolutePostUrl)
      }}
    </summary>
    <content xml:lang="en" type="html">
      {{
        post.templateContent
          | htmlToAbsoluteUrls(absolutePostUrl)
      }}
    </content>
  </entry>
  {%- endfor %}

You can have a look at the full Nunjucks template file I use, to generate my Atom feed, on GitHub.

XML in style

Having a look at my feed file directly in the browser looks somewhat like the following.

Screenshot of the raw XML of my feed, as typically presented by browsers when opening an XML file, with a message that there is no associated style information.
Not a particular user-friendly view of the feed.

Even though this is a fairly standard view for XML on the web, it is not very user-friendly and little useful in general. With this, greet XSLT (Extensible Stylesheet Language Transformations), a mouth full of words where "Stylesheet" is the important part for me.

XSL … T?

To keep it short, XSLT is an XML like language to transform XML documents. Think of reformatting a document, or in that case, styling a document. Taking the XML feed and transforming it into an HTML document, getting the option to use styles I already defined on my website.

When defining a xsl:stylesheet I need to define what namespaces I want to use — xsl for operations like loops with xsl:for-each, and atom, used to access elements in the feed. Defined are those by referencing the associated schema. I could use any name really, but I stick to xsl and atom.

<xsl:stylesheet
  version="3.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:atom="http://www.w3.org/2005/Atom">

In my mind I think of those as "imports of elements and functions into a namespace". A declaration like the one above could correlate to the following pseudocode:

import * as xsl from "http://www.w3.org/1999/XSL/Transform";
import * as atom from "http://www.w3.org/2005/Atom";
This is at least how I think about looking at the XSL.

The general idea then is to output HTML to view it in the browser. This can be done by setting the xsl:output method to HTML.

<xsl:output
  method="html"
  version="1.0"
  encoding="UTF-8"
  indent="yes"
/>

To actually create a transformation template, I need to match an element from my feed.xml file. The root element is the feed element, as it is under the Atom namespace I need to add the atom: prefix to the element I want to select.

<xsl:template match="atom:feed">
  <!-- Here comes the HTML --->
</xsl:template>

Styling XML

To properly set things up, I create a feed.xsl file referenced by my feed.

<!-- /feed.xml -->

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet
    href="/feed.xsl"
    type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <!-- ... -->
</feed>

The important part here is:

<?xml-stylesheet
    href="/feed.xsl"
    type="text/xsl"?>

The base of the /feed.xsl looks as follows, setting up the xsl and atom namespace and enabling HTML output.

<!-- /feed.xsl -->

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
    version="3.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:atom="http://www.w3.org/2005/Atom">
  <xsl:output
    method="html"
    version="1.0"
    encoding="UTF-8"
    indent="yes"
  />
  <xsl:template match="atom:feed">
    <!-- nothing here, yet -->
  </xsl:template>
</xsl:stylesheet>

The content that goes into xsl:template will be HTML. The root element being html, having a head and body, and everything else an HTML document needs.

I use the same base HTML structure I use everywhere for my website, including a link to my CSS stylesheet, giving the feed the same look and feel as the rest of my site.

<link rel="stylesheet" href="/style.css" />

Some XSLT basics

The big question for sure is now how to access the contents of the feed and iterate over entries?

Accessing values

Value access works through XPath with xsl:value-of. The root of the feed is already selected through the xsl:template, so accessing values inside, like the tittle, does not need to repeat that path*.

<xsl:template match="atom:feed">
  <title>
    <xsl:value-of select="atom:title" /> Feed
  </title>
</xsl:template>
Reading the title from the feed and using it as the HTML document title.

Defining attributes

Let's say I want to read the blog link from the feed and define it on an a element. This uses the xsl:attribute inside the a tag with the attribute name href.

<a title="To my blog!">
  <xsl:attribute name="href">
    <xsl:value-of
      select="atom:link[2]/@href" />
  </xsl:attribute>
  <xsl:value-of select="atom:title" />
</a>

Inside the xsl:attribute the actual value needs to be selected via xsl:value-of, using XPath again. The path atom:link[2]/@href means: "Select the second ([2]) link element, read the value of the href attribute (@)". Elements of a path are separated by a forward slash /.

Multiple xsl:attribute can be used, as they will all set the defined attribute on the parent node.

XSL loops

Loops work by using xsl:for-each, selecting an element that occurs multiple times. Every post is represented by an entry element inside the feed, therefore atom:entry will be selected.

<xsl:for-each select="atom:entry">
  <!-- for every entry ... -->
</xsl:for-each>

Conditions

Using xsl:if conditions can be created via a XPath expression that can be evaluated to a boolean value, for example testing if an element exists.

<xsl:if test="atom:updated">
  <!-- If `updated` exists, do this ... -->
</xsl:if>

Full example

Of course there is so much more XSLT can do that I cannot cover here (it is a Turing-complete language after all), but those mentioned elements is all I needed for my feed. The full XSLT stylesheet can be found on GitHub.

Having implemented the stylesheet for my feed, accessing the XML file directly in the browser looks way more appealing now, just like the rest of my website, thanks to reusing the existing CSS file.

This is how an XML feed can look like? 🔥

Bonus: Pretty sitemap

While I was at it, I also added a base style for my XML sitemap. Here a shortened version of it, showing all the important parts. A full version of the XSLT file can again be found on GitHub.

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
    version="3.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:sm="http://www.sitemaps.org/schemas/sitemap/0.9">
  <xsl:output
    method="html"
    version="1.0"
    encoding="UTF-8"
    indent="yes"
  />
  <xsl:template match="sm:urlset">
    <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
        <title>Martin Fieber's Sitemap</title>
        <meta
          name="viewport"
          content="initial-scale=1.0, width=device-width" />
        <link
          rel="icon"
          href="/favicon.ico"
          sizes="any" />
        <link rel="stylesheet" href="/style.css" />
      </head>
      <body>
        <div class="content">
          <header>
            <h1>
              <a
                  href="/"
                  title="Take my back to start!">
                Martin Helmut Fieber
              </a>
            </h1>
          </header>
          <main>
            <h2>Links</h2>
            <ul>
              <xsl:for-each select="sm:url">
                <li>
                  <a>
                    <xsl:attribute
                        name="href">
                      <xsl:value-of
                        select="sm:loc" />
                    </xsl:attribute>
                    <xsl:attribute
                        name="title">
                      Updated:
                      <xsl:value-of
                        select="sm:lastmod" />
                    </xsl:attribute>
                    <xsl:value-of
                      select="sm:loc" />
                  </a>
                </li>
              </xsl:for-each>
            </ul>
          </main>
        </div>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

Epilogue

Feeds are awesome, and if you can, you should consider adding one to your site if you have one. And if you're not already using feeds, consider this as well. Getting the content you really like, directly form the source, through a service or app you want to use, really changed the quality of content I consume.

I certainly don't want to miss feeds anymore. Until then 👋🏻

← Show all blog posts