How to Build an RSS Feed with F# and Falco

Date: 2025-04-23 | craft | create | falco | fsharp | hamy-xyz | iamhamy | tech |

DISCLOSURE: If you buy through affiliate links, I may earn a small commission. (disclosures)

I recently added an RSS feed to my blog so people with RSS readers can get updates easier.

In this post we'll walk through creating an RSS feed using F# and serving it with Falco.

What is an RSS Feed?

An RSS feed (Really Simple Syndication) is a way to get updates from a source. Typically it's provided by content sources so that consumers can more easily determine if new content exists and where to find it.

It's typically an XML format that contains:

  • Info about the source - title, description, url
  • List of updates from the source - often new content like posts or podcasts with title, description, url

This provides a simple way for consumers to "subscribe" to an RSS feed so that they can fetch the latest updates.

Example RSS feeds:

F# + Falco Markdown Blog

We'll be building the RSS feed onto a markdown blog served with F# + Falco:

  • Web Server: F# + Falco
  • Markdown to HTML: Markdig and YamlDotnet

In this post we'll be focused on the RSS feed portion so won't go into detail about how the markdown blog itself is built. For more details, see my previous post: Build a Simple Markdown Blog with F# and Falco.

Note that this is exactly how my current blog is built and mirrors its RSS feed.

We'll be going over the relevant code here but HAMINIONs members get access to the full project source code (GitHub) as well as code from dozens of other example projects we build here.

Building an RSS Feed with F#

We have two primary data types:

  • RssItem - A single item in the feed
  • RssChannel - Represents the feed itself (and contains RssItems)

Code: PostPresentation.RssFeed (GitHub)

module RssFeed = 
    open System.Xml.Linq

    type RssItem = {
        Title : string
        Link : string
        Description : string
        PubDate : DateTime
    }

    type RssChannel = {
        Title : string
        Link : string
        Description : string
        Language : string
        LastBuildDate : DateTime
        Items : RssItem list
    }

    let rssItemToXml (item : RssItem) =
        XElement(XName.Get("item"),
            XElement(XName.Get("title"), item.Title),
            XElement(XName.Get("link"), item.Link),
            XElement(XName.Get("description"), item.Description),
            XElement(XName.Get("pubDate"), item.PubDate.ToString("r")),
            XElement(XName.Get("guid"), 
                item.Link)
        )

    let rssToXml (channel : RssChannel) =
        let rss = 
            XElement(XName.Get("rss"),
                XAttribute(XName.Get("version"), "2.0"),
                XElement(XName.Get("channel"),
                    XElement(XName.Get("title"), channel.Title),
                    XElement(XName.Get("link"), channel.Link),
                    XElement(XName.Get("description"), channel.Description),
                    XElement(XName.Get("language"), channel.Language),
                    XElement(XName.Get("lastBuildDate"), channel.LastBuildDate.ToString("r"))
                )
            )
        
        // Add all items to the channel
        let channelElement = rss.Element(XName.Get("channel"))
        channel.Items
        |> List.map rssItemToXml
        |> List.iter (fun item -> channelElement.Add(item))
        
        // Return the complete RSS document
        XDocument (rss)


    let rss_channel (serviceTree: PostServiceTree) = 
        let post_list = 
                serviceTree
                    .PostService()
                    .GetOrderedPosts
                        0
                        25

        let rss_list = 
            post_list.Posts
            |> List.filter (fun p -> 
                (p.Date.ToDateTime(TimeOnly(0, 0, 0)) < DateTime.UtcNow))
            |> List.map (fun p -> 
                {
                    Title = p.Title
                    Link = $"https://hamy.xyz/blog/{p.Id}"
                    Description = p.Title
                    PubDate = (p.Date.ToDateTime(TimeOnly(0, 0, 0)))
                })

        {
            Title = "HAMY LABS"
            Link = "https://hamy.xyz/blog"
            Description = "Technomancer building Simple Scalable Systems."
            Language = "en-us"
            LastBuildDate = DateTime.UtcNow
            Items = rss_list
        }

    let handle_rss_feed
        (serviceTree : PostServiceTree)
        : HttpHandler = 
        let rss_channel_data = rss_channel serviceTree

        fun ctx ->
            let feed = rss_channel_data |> rssToXml
            let xmlContent = feed.ToString()

            ctx
            |> Response.withContentType "text/xml; charset=utf-8"
            |> Response.ofString (System.Text.Encoding.UTF8) xmlContent
  • handle_rss_feed - Entrypoint that gets the channel data, converts it to xml then string, and finally returns it with content type text/xml
  • rss_channel - Helper to build the RSS Channel data
  • rssToXml - Given an RssChannel, turn that into XML
  • rssItemToXml - Creates XML for a given item

The last thing we need to do is configure our route at /blog/rss:

get "/blog/rss" (
    RssFeed.handle_rss_feed blogServiceTree
)

Next

So that's how I built an RSS Feed for my F# + Falco markdown blog.

As a reminder: HAMINIONs get access to the full project source code (GitHub).

If you're curious how I build fullstack webapps with F# + Falco, checkout CloudSeed my F# project boilerplate.

If you liked this post you might also like:

Want more like this?

The best way to support my work is to like / comment / share for the algorithm and subscribe for future updates.