Thursday, August 25, 2011

Pass-Thru Node.js and CouchDB

‹prev | My Chain | next›

Up tonight, I try to get started with a simple node.js / CouchDB application. I am a big fan of CouchDB with node.js because CouchDB speaks HTTP natively. There is no need for middleware or data drivers with CouchDB—I can just make HTTP requests and process the response or send it along to the client.

I already have the latest node.js installed and a CouchDB server running.

My first step is to create a simple express.js application to play with:
➜  repos  express calendar   
   create : calendar
   create : calendar/package.json
   create : calendar/app.js
   create : calendar/public/stylesheets
   create : calendar/public/stylesheets/style.css
   create : calendar/public/javascripts
   create : calendar/public/images
   create : calendar/views
   create : calendar/views/layout.jade
   create : calendar/views/index.jade
In the new "calendar" app directory, I need to install the express and jade packages from npm:
➜  calendar git:(master) ✗ npm install express jade
jade@0.14.2 ./node_modules/jade 
express-unstable@2.4.3 ./node_modules/express
├── mime@1.2.2
├── connect@1.6.0
└── qs@0.3.1
My next step is to use the Futon admin interface to create a database. I fancy a calendar app, so I name my database accordingly:


Next, I create a calendar event:


And an event for tomorrow:


To allow my simple express.js app to pass those events back to the browser, I need to establish a route for all events. In that route, I access the special _all_docs resource in CouchDB to pull back all records in the calendar DB (I prolly would not do that in a larger DB). Once the Couch DB response comes back, I write the data back to the browser:
app.get('/events', function(req, res){
  var options = {
    host: 'localhost',
    port: 5984,
    path: '/calendar/_all_docs'
  };

  http.get(options, function(couch_response) {
    console.log("Got response: %s %s:%d%s", couch_response.statusCode, options.host, options.port, options.path);

    res.contentType('json');

    // Send all couch data to the client
    couch_response.on('data', function (chunk) {
      res.write(chunk);
    });

    // When couch is done, so is this request
    couch_response.on('end', function (chunk) {
      res.end();
    });
  }).on('error', function(e) {
    console.log("Got error: " + e.message);
  });
});
That's a bit of work establishing 'data' and 'end' listeners. Fortunately, node has an answer for this case in the pipe method for all stream objects:
  http.get(options, function(couch_response) {
    console.log("Got response: %s %s:%d%s", couch_response.statusCode, options.host, options.port, options.path);

    couch_response.pipe(res)
  })
Any events emitted by couch_response will be sent to the original Response object. The result of calling accessing the /events resource is:


As can be seen in the screen shot, the actual event data is not being returned—just IDs and other metadata. To get the full event, I can create another express.js route with a similar callback:
app.get('/events/:id', function(req, res){
  var options = {
    host: 'localhost',
    port: 5984,
    path: '/calendar/' + req.params.id
  };

  http.get(options, function(couch_response) {
    console.log("Got response: %s %s:%d%s", couch_response.statusCode, options.host, options.port, options.path);

    couch_response.pipe(res);
  }).on('error', function(e) {
    console.log("Got error: " + e.message);
  });
});
This route makes use of some nifty parameter assignments in express.js. Named parameters in the route (the :id in /events/:id) are made available in the request params object (e.g. req.params.id). That bit of coolness aside, this is nearly identical to the all-events route from above.


That is all well and good, but I do not think that I want my client making dozens of calls to this resource for each event on a particular calendar. Instead, I go back into my /events route and add include_docs=true to the URL. This includes the documents (which are relatively small) along with the meta data:


Cool beans. That is a good stopping point for tonight. Tomorrow I will hook those into jQuery Ajax calls to build a month-view calendar. And then the fun begins with a backbone.js equivalent.

Day #124

5 comments:

  1. Hello Chris,

    I have a quick question please. I am just starting on this series and I am a total CouchDB n00b. I notice that the screen-shots for 'I create a calendar event' and 'an event for tomorrow' are the same. Is this intended? Could you pls. take a second and pls. fix or point me to some related info.

    thanks a bunch

    ReplyDelete
  2. If you look closely, the two screenshots are different. The attribute keys (title, startDate, etc) are the same, but the values (2011-08-25, 2011-08-26) are different.

    Good luck following along with the series. I didn't really write it with the idea that people would try to recreate the code / DB. Hopefully there won't be too many gaps along the way.

    Source is here: https://github.com/eee-c/Funky-Backbone.js-Calendar (much of it is in branches).

    Hope that helps.

    -Chris

    ReplyDelete
  3. Yes, it does. Thanks a lot

    I am planning to work through the series since I like to work in a similar fashion when trying to explore a new topic /language.

    I will try to keep the questions to a minimum though :D

    thanks again

    ReplyDelete
  4. Hi Chris
    I am really new to this so need a little help. Actualy i am trying to get data from couchdb n display it on the page using backbone.js. so in your example after u create the calender database you hav used the backbone.js code to route the events ryt? i want to knw where exactly do u execute that code..?? where do u have to write it?

    ReplyDelete
    Replies
    1. Actually, there isn't any Backbone code in this post. All of this was in the express.js webserver which I'm using here to read from CouchDB and send to the browser in a REST-like JSON format. The routing that I talk about in this post is server-side, express.js routing -- not Backbone routing.

      I start the converting to Backbone.js a few days later: http://japhr.blogspot.com/2011/08/converting-to-backbonejs.html.

      The end result is at: https://github.com/eee-c/Funky-Backbone.js-Calendar, but if you keep following the "next" links at the top of this page, I try to explain my entire process -- perhaps it could be of some use?

      Delete