Computer Things
My Reflective Software business partner and I are currently looking for our next client. Learn more.

Creating an RSS feed in Elixir and Phoenix

See other articles in the “Elixir” topic.

Hand-rolling an RSS feed with Elixir and Phoenix is pretty straightforward. You just need a list of feed items, a controller, a template, and a view. In this article, I’ll assume the list of items is a list of blog articles.

Articles

A simple way to store your articles is in a list somewhere. A more complicated way is to store it in a database. I’ll assume we’re storing it in a list.

Elixir
defmodule Core.Articles do
  @articles [
    %{title: "Apples", description: "Red or green fruit", published_at: ~U[2024-02-25 00:00:00Z], slug: "apples"},
    %{title: "Bananas", description: "Yellow fruit", published_at: ~U[2024-02-26 00:00:00Z], slug: "bananas"},
    %{title: "Cherries", description: "Red fruit", published_at: ~U[2024-02-27 00:00:00Z], slug: "cherries"}
  ]

  def articles, do: @articles
end

Controller

All the controller needs to do is render an XML template:

Elixir
defmodule Web.RssController do
  use Web, :controller

  def index(conn, _params) do
    render(conn, "index.xml", articles: Core.Articles.all(), host: conn.host, port: conn.port)
  end
end

Template

The template needs to include some header information, and then iterate over the articles:

XML
<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Favorite Fruit</title>
    <description>A list of fruit</description>
    <link>https://example.com/</link>
    <lastBuildDate><%= pub_date(@articles) %></lastBuildDate>
    <pubDate><%= pub_date(@articles) %></pubDate>
    <ttl>1800</ttl>
    <atom:link href="https://example.com/rss" rel="self" type="application/rss+xml"/>

    <%= for article <- @articles do %>
      <item>
        <title><%= article.title %></title>
        <description><![CDATA[ <%= article.description %> ]]></description>
        <link><%= "https://example.com/#{article.slug}" %></link>
        <guid isPermaLink="false"><%= article.slug %></guid>
        <pubDate><%= pub_date(article) %></pubDate>
      </item>
    <% end %>
  </channel>
</rss>

View

The view needs to implement a couple helper functions for the template.

Elixir
defmodule Web.RssView do
  use Web, :view

  def pub_date(nil), do: ""
  def pub_date(articles) when is_list(articles), do: List.first(articles) |> pub_date()
  def pub_date(article), do: format_rfc822(article.published_at)

  def format_rfc822(date_time), do: Calendar.strftime(date_time, "%a, %d %b %Y %H:%M:%S %Z")
end

Wrap up

Add a route to your router:

Elixir
get "/rss", Web.RssController, :index

and add a link to the “head” section of your “app.html.heex” template:

HEEX
<link rel="alternate" type="application/rss+xml" title="RSS feed for example.com" href="/rss" >

and you’re all done. Easy to do without having to add an external dependency.


More “elixir” articles

Make Elixir Tests Faster
Tips on making your Elixir tests run faster.
Phoenix Project Layout
A nonstandard way to lay out your Phoenix project.
Querying HTML and XML in Elixir with HtmlQuery and XmlQuery
Find and extract information from HTML and XML using Elixir.
Web.Paths
A "Web.Paths" module in your Elixir Phoenix app can be a helpful indirection.
Why Boundary?
About the Boundary library, and why I highly recommend adding it to every Phoenix project from the very beginning.