O'Reilly

Rolling Your Own RSS Feed with Express and Jade

By on  

RSS feeds are a great way to facilitate a loyal readership. In fact, as I write this, the RSS feed in David's sidebar is touting over 11,400 subscribers. Hitting the front page of Hacker News is always nice, but for most sites that's not going to translate into a reliable source of traffic. Getting each and every post in front of thousands of intentional subscribers though (who have their own followers on Twitter, Google+, etc.)? That's a traffic generator.

There's really only one gotcha to RSS feeds - you actually have to have one. A little over a month ago I launched a new blog, DevSmash. My shiny new site had been public for less than 48 hours when I received a tweet asking where my RSS feed was. Not if I had an RSS feed, but where was my RSS feed. You see, I had the good fortune of receiving some decent promotion on one of my first posts. Users started showing up, and evidently some of them looked for some means of subscribing. I have an RSS feed now, of course, but all those users who showed up in that first week or so - they're long gone. Moral of the story: RSS feeds are great, but you need it before your readers show up.

Alright - let's call that sufficient context. DevSmash is built on top of all the newfangled goodness you could ask for: Node.js, Express, Jade, Stylus, MongoDB, Mongoose, etc. It's a stack that I absolutely love hacking on, but it exposes an intentionally lean feature set, so "rolling your own xyz" often comes with the territory. Such was the case with my RSS feed. This post provides an overview of how I built the DevSmash RSS feed, and I hope that it will be useful to others building on this increasingly popular stack.

Cast of Characters

Before we get started, let's do a quick overview of the main technologies we'll be using:

Express

From the Express home page: "Express is a minimal and flexible node.js web application framework, providing a robust set of features for building single and multi-page, and hybrid web applications." What TJ Holowaychuk is too modest to say here, is that Express has become the de facto standard for building web apps on Node. There are of course other options, but you definitely owe it to yourself to check it out if you haven't already.

Website: http://expressjs.com/

Jade

From the Jade readme: "Jade is a high performance template engine heavily influenced by Haml and implemented with JavaScript for node." This is my go-to template engine of choice - terse, feature rich, and a syntax that reads as well as it writes.

Website: http://jade-lang.com/

Mongoose

From the Mongoose GitHub repo: "Mongoose is a MongoDB object modeling tool designed to work in an asynchronous environment." In other words, Mongoose provides a model layer for interacting with your MongoDB collections from Node.

Website: http://mongoosejs.com/

Note: It's relatively inconsequential that we're using Mongoose in this post. The concepts should translate well enough to however you're managing your persistence.

RSS Requirements

One last thing before we dig into the code - let's identify our basic requirements for our RSS feed:

  1. The feed should include the most recent 20 published posts.
  2. Output should be a valid RSS 2.0 feed (I'm personally using the W3C feed validator to verify).

Simple enough.

The Code

For the sake of this article, we only need to be concerned with three files:

  • blog-post.js: Our BlogPost Mongoose Model (implementation details aren't too important for this article, but it's included for the sake of completeness).
  • feed.js: Our route handler (responsible for fetching posts from the database and feeding them to our view).
  • rss.jade: Our RSS template (responsible for turning our posts into a valid RSS 2.0 feed).

blog-post.js

We won't spend too much time talking about this file - it's purely here for reference since we'll be interacting with it later on.

var mongoose = require('mongoose'),
    Schema = mongoose.Schema;

var BlogPostSchema = new Schema({
    title: { type: String, required: true, trim: true },
    slug: { type: String, required: true, lowercase: true, trim: true, index: { unique: true } },
    body: { type: String, required: true },
    teaser: { type: String, required: true },
    author: { type: String, required: true, trim: true },
    published: { type: Boolean, required: true, default: false },
    createdAt: { type: Number },
    updatedAt: { type: Number }
});

// update timestamps on save
BlogPostSchema.pre('save', function(next) {
    this.updatedAt = Date.now();
    if (this.isNew) this.createdAt = this.updatedAt;
    next();
});

// create and export our model
module.exports = mongoose.model('BlogPost', BlogPostSchema);

feed.js

A common Express convention for route handlers is to put them into a dedicated routes/ folder. In my own apps I generally have my route files export a single function that accepts the Express application instance, like so:

// some-route-handler.js
module.exports = function(app) {
    app.get('/some/path', function(req, res, next) {
        // handler logic
    });
};

With a code structure like this in place, your main app.js file simply needs a line like the following:

require('./routes/some-route-handler')(app);

Alright then, here's what a functional RSS handler actually looks like:

var BlogPost = require('../lib/model/blog-post');
module.exports = function(app) {
    app.get('/feed/rss', function(req, res) {
        BlogPost.find({})
            .sort('-publishedAt')
            .where('published', true)
            .limit(20)
            .select('title slug publishedAt teaser')
            .exec(function(err, posts) {
                if (err) return next(err);
                return res.render('rss' {
                    posts: posts
                });
            });
    });
};

As can be seen, there's not much need for cruft around populating our RSS feed. We simply query for the most recent 20 published posts, and render them with our RSS template. Which brings us to...

rss.jade

While Jade's primary use case is generating HTML output, it is just as handy at generating XML. Here's what our Jade template looks like:

doctype xml
rss(version='2.0', xmlns:atom='<a href="http://www.w3.org/2005/Atom" rel="nofollow">http://www.w3.org/2005/Atom</a>')
    channel
        title DevSmash
        link <a href="http://devsmash.com" rel="nofollow">http://devsmash.com</a>
        atom:link(href='<a href="http://devsmash.com/feed/rss" rel="nofollow">http://devsmash.com/feed/rss</a>', rel='self', type='application/rss+xml')
        description Developers talking about stuff that developers like to talk about.
        language en-US
        if posts.length
            lastBuildDate= new Date(posts[0].publishedAt).toUTCString()
        each post in posts
            item
                title= post.title
                link <a href="http://devsmash.com/blog/#{post.slug}" rel="nofollow">http://devsmash.com/blog/#{post.slug}</a>
                description
                    | <![CDATA[
                    | !{post.teaser}
                    p: a(href='<a href="http://devsmash.com/blog/#{post.slug}')!=" rel="nofollow">http://devsmash.com/blog/#{post.slug}')!=</a> 'Read more &raquo;'
                    | ]]>
                pubDate= new Date(post.publishedAt).toUTCString()
                guid(isPermaLink='false') <a href="http://devsmash.com/blog/#{post.slug}" rel="nofollow">http://devsmash.com/blog/#{post.slug}</a>

The Jade syntax may look a little foreign if this is your first time seeing it, but for the most part things are fairly self-explanatory. There are a few things worth pointing out though:

  • The atom stuff isn't strictly required, but was suggested by the W3C feed validator.
  • Outputting the post body (or teaser in this case) requires some special care. You can't encode the markup, or you'll simply see encoded HTML in your RSS reader, but at the same time we need to protect the XML. The standard solution, then, is to wrap the post markup inside of CDATA tags.

And there you have it! Not even 40 lines of code (not counting the model) for a custom RSS feed. I hope this was helpful, and I'd love to hear any thoughts, questions, or concerns in the comments!

Jeremy Martin

About Jeremy Martin

Jeremy Martin is a software developer and Open Source Evangelist at his day job, a Node.js contributor and MongoDB fan boy in his spare time, and husband to the greatest gal on the planet.

Track.js Error Reporting

Recent Features

  • An Interview with Eric Meyer

    Your early CSS books were instrumental in pushing my love for front end technologies. What was it about CSS that you fell in love with and drove you to write about it? At first blush, it was the simplicity of it as compared to the table-and-spacer...

  • 5 HTML5 APIs You Didn&#8217;t Know Existed

    When you say or read "HTML5", you half expect exotic dancers and unicorns to walk into the room to the tune of "I'm Sexy and I Know It."  Can you blame us though?  We watched the fundamental APIs stagnate for so long that a basic feature...

Incredible Demos

  • MooTools Window Object Dumping

    Ever want to see all of the information stored within the window property of your browser? Here's your chance. The XHTML We need a wrapper DIV that we'll consider a console. The CSS I like making this look like a command-line console. The MooTools JavaScript Depending on what you have loaded...

  • Form Element AJAX Spinner Attachment Using MooTools

    Many times you'll see a form dynamically change available values based on the value of a form field. For example, a "State" field will change based on which Country a user selects. What annoys me about these forms is that they'll often do an...

Discussion

  1. Thanks for the article – I am working on a site and currently using the node package ‘rss’ for the feeds side of things, but given how simple you make it look, I might try this instead.

    Especially as the package does not support alternate link types – whereas I can do it just by amending the template :)

  2. Thanks for the article, I am working on a site and used the ‘rss’ package, but would be good to extend it to support additional media links – I think I might just do what you have done instead :)

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!

Recently on David Walsh Blog

  • Best Tools and Resources for Web Professionals in 2015

    Looking for the right resources to help you satisfy the needs of your clients? On the lookout for the best tools to help you increase your revenue? Searching for the right software to help you improve your business? Well, then you’ve come to the right place....

  • JavaScript Polling

    Polling with JavaScript is one of those ugly but important functions within advanced front-end user experience and testing practices.  Sometimes there isn't the event you can hook into to signify that a given task is complete, so you need to get your hands dirty and simply poll for...

  • OSCON Portland:  Conference Giveaway and Discount!

    O'Reilly puts on the best web industry conferences in the world.  These conferences include Fluent Conference, Velocity Conference, and the upcoming OSCON in Portland, Oregon from July 20-24.  Open Source Convention (OSCON) is a conference that focuses specifically on open source developers and the tools and possibilities...

  • Prevent Chrome from Translating a Page

    A while back I shared my favorite Google Chrome extension:  Google Art Project.  I've enjoyed seeing beautiful art when I open a new tab -- it's brought genuine happiness to my day, however small that happiness may be.  About a week ago, however, the art presented had...

  • Create Any Type Of Website With These Multi-Purpose Themes

    We have selected what we believe are the very best multipurpose WordPress themes on the market today. Our list contains a number of best sellers, several newcomers that are proving to be highly popular, and a few themes that are ideal for creating the types of...