Friday, August 26, 2011

A Simple Node.js + CouchDB Calendar

‹prev | My Chain | next›

Last night, I got a nice, little node.js and CouchDB app thrown together. The node (really express.js) app serves up simple HTML and also passes through requests to the CouchDB database. Tonight I would like to serve up a static HTML calendar and populate events from the CouchDB store.

For as long as I have been building HTML calendars, I have always put the ISO8601 date on the day cells. In Jade templating this looks like:
...
  tr#week4
    td.sunday
    td.monday 22
    td.tuesday
    td.wednesday
    td#2011-08-25.thursday
    td#2011-08-26.friday
    td.saturday
...
The resultant HTML is then:
And the resultant page looks like:
The benefits of using ISO 8601 are numerous, which is why it is the de facto standard for XML and JSON dates and times. Since CouchDB is returning JSON, I can be pretty sure that it will be returning ISO 8601 (especially since I created the data in the first place). By identifying the date cells by ISO 8601, it will make it easy to tie date records to date cells. Let's have a look at what I mean...

Accessing the /events resource in my app returns:
{"total_rows":2,"offset":0,"rows":[
  {"id":"fdbed27594feb433c74e82eb910015e0",
   "key":"fdbed27594feb433c74e82eb910015e0",
   "value":{"rev":"2-b7c22d428e648a6cdd2978c213f79ec0"},
   "doc":{"_id":"fdbed27594feb433c74e82eb910015e0",
          "_rev":"2-b7c22d428e648a6cdd2978c213f79ec0",
          "startDate":"2011-08-25",
          "title":"create blog post",
          "description":"talk about node and CouchDB"}},
  {"id":"fdbed27594feb433c74e82eb91001f45",
   "key":"fdbed27594feb433c74e82eb91001f45",
   "value":{"rev":"1-2b18432cf6e63b82c6507ff28af9724c"},
   "doc":{"_id":"fdbed27594feb433c74e82eb91001f45",
          "_rev":"1-2b18432cf6e63b82c6507ff28af9724c",
          "startDate":"2011-08-26",
          "title":"blog again",
          "description":"add backbone into the node + couch mix"}}
]}
(this is just a pass-thru to CouchDB's _all_docs?include_docs=true)

To get those events into the calendar, I perform a jQuery getJSON call inside a document-ready:
  $(function() {
    $.getJSON('/events', function(data) {
      $.each(data.rows, function(i, rec) { add_event(rec.doc) });
    });
  });
For each of the rows in the events returned from CouchDB, I extract the document (the event itself) and make a call to add_event().

The add_event() function then exploits the fact that the cells in my calendar are identified with an ISO 8601 date:
  function add_event(event) {
    var date = event.startDate,
        title = event.title,
        description = event.description;

    $('#' + date).html(
      '<span title="' + description + '">' +
        title +
      '</span>'
    );
  }
If the startDate from CouchDB is 2011-08-26, then this function finds the correct cell via a jQuery $('#2011-08-26') selector. If that selector is found, then the inner HTML is replaced with the event's title (and the description in a <span> title attribute). If the calendar event is for a date not currently displayed, no worries, the selector returns an empty wrapped set, in which case the html() has nothing to do.

The result is a rather snappy:
Nice. There is no ability to modify or remove elements just yet, but it was quite easy to get this calendar populated quickly. Up tomorrow, I think I shall begin exploring doing this again, but with Backbone.js.


Day #126

5 comments:

  1. First, loved the clarification as to why ISO8601 is the right way to go with dates. There are still arguments of using millis-from-epoc but you lose a lot of readability and some mild confusion about millis-since-epoch or secs-since-epoch as used by other systems.

    Second, really liked the little trick with ID'ing the cells by their date then adding the descriptions using that shortcut.

    Simple and slick.

    Out of curiosity, which couchdb lib do you prefer to use in node.js? I am just starting to dabble and noticed something like 11 modules for "couchdb" in the node.js module list.

    ReplyDelete
  2. Glad you liked it!

    Honestly, I just use pure http for accessing and interacting with Couch. See my previous day's post for an example: http://japhr.blogspot.com/2011/08/pass-thru-nodejs-and-couchdb.html. Even when I'm doing it in Ruby, I tend to stick with RestClient or something very close to pure http. Much of Couch's power comes from its simplicity and I haven't had reason to add anything on top of it.

    ReplyDelete
  3. Chris, thanks for the reply.

    In my hunt for something really thin (no ORMs) I ran across nano, have you played with this at all? Seems promising: http://writings.nunojob.com/2011/08/nano-minimalistic-couchdb-client-for-nodejs.html

    ReplyDelete
  4. Yah, I should have mentioned nano. I have played with it and do like its minimalistic approach. I still personally prefer pure http myself, but nano is probably a better option for teams / larger projects.

    ReplyDelete
  5. I am always a fan of learning bottom-up so I understand better what each library is doing and what the platform does.

    I'll stick with vanilla then and just whenever I have pain points, look for a library that eases that pain as opposed to running out of the gates with 10 libraries hung around my neck with no great idea of what any of them do.

    Appreciate the pointers Chris!

    ReplyDelete