Building an RSS Reader With Meteor
June 27, 2013
Meteor is a nifty Javascript based platform that features live page updates as data is changed across browsers and users. With the demise of Google Reader, I thought an RSS reader might be a good application to put Meteor through its paces. Turns out Meteor does a fantastic job and is a lot of fun to work with.
I named my RSS reader Ocular. (Feel free to kick the tires.) With my design background and reading habits in mind, Ocular behaves differently from RSS readers most of us are familiar with. RSS is an amazing tool and a fantastic way to deliver information. The only down-side is the lack of visual design accompanied with most feeds. Ocular solves this by displaying the actual article associated with that feed item.
By showing the actual article, the content's original design is preserved, allowing the user to read content as the author intended. More added bonuses include the ability to see and interact with comments, and if you're generating revenue through advertising, those ads will also be visible to the user.
The other major difference between Ocular and traditional RSS readers is the way Ocular handles new feed content. I'm a very casual reader when it comes to RSS. I don't read every single feed item that is pushed out by major content publishers like Engadget or the NY Times. With that in mind, Ocular only checks for feed updates when you're running the app. In my case, I keep Ocular open in a separate tab during the day while I'm working. At night, Ocular is closed and not pulling in updates. Turns out, this works out great because I'm still not missing any content. Sites that post less frequently still get picked up because Ocular is pulling in feed items based on the date of the last item that was pulled in. If one of my favorite sites publishes a few times overnight, those posts will be pulled in in the morning. On the opposite end of the spectrum, if a site like The Verge publishes 30 items overnight, it's possible I might miss a few of the older feed items when I fire up Ocular in the morning. But, I'm not concerned about that because I don't want to read that much content to begin with.
Aside from those differences, Ocular has features you would expect in an RSS reader: Feed lists, unread counts, the ability to mark favorites, etc.
Meteor technical details
Here are some methods specific to Meteor that I've found interesting when building this project. I'm somewhat of a novice when it comes to Javascript, so please, feel free to suggest improvements.
Pulling in RSS feeds
Probably my favorite functionality in this app is all the feed updates happening in the browser using the Google Feed API.
Here's a shortened example:
.ajax({ type: "GET", url: "http://ajax.googleapis.com/ajax/services/feed/load?v=1.0&num=10&q="+url, dataType: "jsonp", success: function( data ) { // If article published date is newer than feed's last published date, add the article to the DB. if ( Date.parse( articles[j]['publishedDate'] ) > lastPublished ) { Meteor.call( 'addArticle', { feedId: feedId, title: articles[j]['title'], url: articles[j]['link'], publishedDate: articles[j]['publishedDate'] }, function( error, result ) { Meteor.call( 'updateReadCount', feedId, 1); }); } } });
If RSS articles are newer than the last saved RSS articles, we tell Meteor to save that article
Meteor.call( 'addArticle' );
and update the unread count
Meteor.call( 'updateReadCount', feedId, 1);
In our model file, those calls look like this:
Meteor.methods({ addArticle: function( options ) { options = options || {}; pubDate = Date.parse( options.publishedDate ); return Articles.insert({ owner: Meteor.userId(), feedId: options.feedId, title: options.title, url: options.url, publishedDate: pubDate, read: false, favorite: false, share: false }); }, updateReadCount: function( feedId, num ) { return Feeds.update( feedId, { $inc: { unreadCount: num } }); } });
The beauty of Meteor is, as soon as those DB entries are updated, the template updates the list of articles and the unread count all without a page refresh. It's really exciting when you see that happen for the first time.
Displaying articles
This is a simple process, but something I had never done before. Using an iframe to house the article, when a user clicks on the feed item they'd like to read, the iframe is populated with the source URL from the feed article and the article is marked as read with a markRead method.
// client.js Template.articleList.events({ 'click .article': function( event ) { Meteor.call( 'markRead', this._id ); } }); // model.js markRead: function( articleId ) { return Articles.update( articleId, { $set: { read: true } }); }
User accounts
A nice add-on tool is the accounts-ui package that makes dealing with user accounts quick and easy. In the template, adding the login/account UI is as simple as adding a single line:
<div class="header"> {{loginButtons}} </div>
Then in the client file we can do things like check to see if the user is logged in and do actions based on that state:
// If user is logged in, show a div. if ( Meteor.user() ) { $( "div" ).show(); }
Pulling in data based on the logged in user looks like this in the client file:
var feeds = Feeds.find({$or: [{owner: Meteor.userId()}]}, {sort: {"title": 1}}).fetch();
That pulls in a list of RSS feeds associated with that user and sorts them alphabetically by title.
Large data
The one drawback facing the current iteration of Ocular is sluggishness around updating large sets of data. If I have an unread count pushing 1,000+ items and I mark all items as read, Meteor takes several seconds to make those changes to each record and apply a "read" class to each html element in the template. I don't know if the hang-up is on the DB side or the template markup side. I'm sure there's a better way to optimize my approach, but that's something I'm still learning.
A great experience
All-in-all Meteor has been a lot of fun. I wish I was doing more to take advantage of the power of live page updates. Something like a list of articles marked as favorites across all users in real time could be fun.
For someone somewhat new to the deep depths of Javascript, I found Meteor to be relatively smooth sailing. Documentation is well done and it was rare that I encountered any major roadblocks. If you're interested in Meteor, definitely give it a try. I'm already thinking about what I can build next.
Update: I get a lot of requests to see the entire application code so I've posted the project to GitHub. I hope this helps others starting out with Meteor.