Skip to content

Instantly share code, notes, and snippets.

@janl
Forked from mikeal/gist:552181
Created August 26, 2010 21:24
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save janl/552288 to your computer and use it in GitHub Desktop.
Save janl/552288 to your computer and use it in GitHub Desktop.

Using CouchDB with node.js

First, get yourself a CouchDB! You can get a free hosted CouchDB in seconds or download a binary from the Couchio downloads page.

Next, install npm because all awesome node packages are in npm :)

Node has better support for HTTP than any language I've ever worked with and coincidentally CouchDB's interface is solely accessible via HTTP. First, let's just talk to CouchDB using an HTTP library and later we'll look at some higher level abstractions.

Talking to CouchDB using request

Go ahead and install request.

$ npm install request

This is a simplified HTTP client that is in pretty wide use among node developers. Let's get a list of all databases in your CouchDB.

var sys = require('sys')
  , request = require('request')
  , h = {accept:'application/json', 'content-type':'application/json'}
  ;

request({uri:'http://myname.couchone.com:5984/_all_dbs', headers:h}, function (err, response, body) {
  console.log(sys.inspect(JSON.parse(body)));
})

If your CouchDB is brand new you'll probably see something like this.

[ '_users' ]

Now let's create a database. You could do this easily in Futon, the web interface for CouchDB, but it's just more fun to do this stuff all in node.

request({uri:'http://myname.couchone.com:5984/dbname', method:'PUT', headers:h}, function (err, response, body) {
  if (err) throw err;
  if (response.statusCode !== 201) throw new Error("Could not create database. "+body);
})

The err parameter is actually socket errors from the HTTP client, any valid HTTP response won't have the err parameter so if you want to validate that we did in fact create the database we need to verify the response code.

If you took your couch out of admin party mode and created an admin account (which you really really should do) then you'll get an error saying that you need to be an admin in order to create new databases. Basic auth is already support in CouchDB and in request so we'll just add those to the url.

request({uri:'http://myaccount:password@myname.couchone.com:5984/dbname', method:'PUT', headers:h}, function (err, response, body) {
  if (err) throw err;
  if (response.statusCode !== 201) throw "Could not create database. "+body;
})

There, simple as that.

Now lets create and get some documents.

var dburi = 'http://myaccount:password@myname.couchone.com:5984/dbname'
request({uri:dburi, method:'POST', body:JSON.stringify({data:'somedata'})}, function (err, resp, b) {
  if (err) throw err;
  if (resp.statusCode !== 201) throw new Error("Could not create document. "+b);
  console.log(b);
})

You'll see a console print something like this.

{"id":"8s7dhf8s7dah8f7sdf8dh", "rev":"1-7sdfy87s", seq:1}

Because we didn't have an id for that document already we did a POST to the dburi and it created a new id for us. If we have something that already has a deterministic id, like say we have the JSON body of a tweet from the twitter streaming API, we should us it's id and use a PUT call.

tweet._id = 'twitter-'+tweet.id
request({uri:dburi+'/'+tweet._id, method:'PUT', body:JSON.stringify(tweet)}, function (err, resp, b) {
  if (err) throw err;
  if (resp.statusCode !== 201) throw new Error("Could not create document. "+b);
  console.log(b);
})

And we should see something like.

{"id":"twitter-192837218", "rev":"1-3dfy87s", seq:2}

We can get this document with a simple GET request.

request({uri:dburi+'/twitter-192837218'}, function (err, resp, b) {
  if (err) throw err;
  if (resp.statusCode !== 200) throw new Error("Could not get document. "+b);
  console.log(sys.inspect(JSON.parse(b)));
})

Or we can grab multiple docs using the all_docs api.

request({uri:dburi+'/_all_docs?include_docs=true'}, function (err, resp, b) {
  if (err) throw err;
  if (resp.statusCode !== 200) throw new Error("Could not get document. "+b);
  console.log(sys.inspect(JSON.parse(b)));
})

The all_docs API also allows startkey and endkey parameters for filtering on the id so a query like this will only give us back our twitter documents..

request({uri:dburi+'/_all_docs?startkey="twitter-"&endkey="twitter-ZZZ"&include_docs=true'}, function (err, resp, b) {
  if (err) throw err;
  if (resp.statusCode !== 200) throw new Error("Could not get document. "+b);
  console.log(sys.inspect(JSON.parse(b)));
})

For creating indexes and querying them I would direct people to the excellent chapter on views in O'Reilly's CouchDB The Definitive Guide which is available in it's entirely online.

Talking to CouchDB using node-couchdb

For some people directly using the HTTP API can be a little verbose. There are already 4 or 5 competing node libraries that provide higher level abstractions but I only have time to talk about one, node-couchdb.

Creating a db is simple.

var couchdb = require('node-couchdb/lib/couchdb')
client = couchdb.createClient(5984, 'myname.couchone.com'),
db = client.db('my-db');
db.exists(function (err, exists) {
  if (!exists) db.create();
})

Creating a document is also simple.

db.saveDoc('my-doc', {awesome: 'couch fun'}, function(er, ok) {
  if (er) throw new Error(JSON.stringify(er));
  console.log('Saved my first doc to the couch!');
});

Getting documents is even simpler.

db.getDoc('my-doc', function(er, doc) {
  if (er) throw new Error(JSON.stringify(er));
  console.log('Fetched my new doc from couch:');
  console.log(doc);
});

Getting all docs is.... you guessed it, SIMPLE!

db.allDocs(function (err, resp) {
  ...
})
db.allDocs({startkey:'twitter-', endkey:'twitter-ZZZ'}, function (err, resp) {
  ...
})

Talking to couch using creationix's couch-client

Tim Caswell just started his own CouchDB client and was gracious enough to contribute the instructions for use below.

The couch-client module is a hybrid between a http request helper and a fully abstracted database driver.

Connecting to a database is simple.

var CouchClient = require('couch-client');
var Users = CouchClient("http://username:password@yourhost:5984/users");

If the database doesn't exist yet you'll get errors, but that's easy to do with the exposed request function.

Users.request("PUT", "/users", function (err, result) {
  
});

As you see, you can make any arbitrary http request using the request() function. It takes between 2 and 4 arguments:

CouchClient#request(method, path[, body][, callback])

If you leave off the callback, it will return a stream instead. The callback if specified gets two arguments callback(err, result). err is an exception object if something went wrong. result is the response from couch already JSON.parseed

Also for higher performance, couch-client has some utility functions that do common tasks with all the nice performance tricks built in. It has connection pooling, http keep-alive, and request combining using couch's bulk APIs

  • CouchClient#save(doc, callback) - save a document
  • CouchClient#get(key, callback) - get a document
  • CouchClient#remove(key/doc, callback) - remove a document
  • CouchClient@changes(since, callback) - watches for changes on the entire database since since and calls callback once per change (experimental)

Examples

Now you should be rockin with some document store deliciousness :)

Here are some more cool examples of using node.js with CouchDB:

Sit's on the twitter streaming API and dumps tweets in to a CouchDB, also auto-follows people.

Higher level CouchDB library with caching.

npm's registry is a Couchapp so all of it's registry modules talk to CouchDB.

The Geddy Web Framework also includes a CouchDB client for it's model system.

If you need any more help I'm on IRC in #node.js and I'll also be at Joyent HQ during most of the Knockout.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment