Reusing Common Patterns

As I already mentioned it, CouchDB is so simple to use that you don’t actually need separate library. Most of the time you can just use request and do http requests.

However, we don’t want to write the same code over and over again for some common tasks. We also don’t want to pass configuration around. It is much better to have it at only one place.

To solve that let’s create a db.js file which will contain our small library for working with our CouchDB.

varrequest=require('request'),querystring=require('querystring')varurl='http://127.0.0.1:5984'// Save a document
exports.save=function(db,doc,done){request.put({url:url+'/'+db+'/'+doc._id,body:doc,json:true,},function(err,resp,body){if(err)returndone('Unable to connect CouchDB')if(body.ok){doc._rev=body.revreturndone(nulldoc)}done('Unable to save the document')})}// Get all documents with the built-in 'All' view
exports.all=function(db,options,done){varparams=querystring.stringify({include_docs:options.include_docs===false?false:true,descending:options.descending,skip:options.skip,limit:options.limit,key:options.key,startkey:options.startkey,endkey:options.endkey,})request({url:url+'/'+db+'/_all_docs?'+params,json:true,},function(err,res,body){if(err)returndone('Unable to connect to CouchDB')done(null,body)})}

For our example we only need two methods. One to save a document, either a new one or an existing one. Another to read all documents from the database by using the built-in ‘All Documents’ view which can give us what we need.

In a complex app we will have a few more methods including some which will help us to better test the application.

Let’s have a look at how we are going to use it in one of the controllers. We are going to implement comments.js from the file structure above.

varexpress=require('express'),router=express.Router()vardb=require('../db')// Submit a comment
router.post('/submit',function(req,res){vardata={_id:(newDate().toJSON())+':'+req.session.user._idmessage:req.body.comment,}db.save('comments',data,function(err,doc){res.redirect('/all')})})// Get all comments
router.get('/all',function(req,res){db.all('comments',{},function(err,data){res.render('comments',{comments:data.rows})})})// Get most recent comments
router.get('/recent',function(req,res){db.all('comments',{limit:20,descending:true},function(err,data){res.render('comments',{comments:data.rows})})})module.exports=router

The built in ‘All Documents’ view returns by default all documents ordered by their id.

Each comment id begins with the date and then the user id. As a result the comments are automatically sorted by date because the toJSON returns the date in a format easily sortable alphabetically.

That is why /recent works. It limits to 20 comments and because they are by default in ascending order, we read the comments from back to front and the result is exactly what we want.

We can implement the user controller in a similar way with those two methods.

If you try the example above right now it will not work. You need first to create the comments database. For this example you can create it from the admin console at http://127.0.0.1:5984/_utils/index.html

Building models with business logic

Reusing our small database library is useful, but we can further improve how we work with CouchDB. For example, you may need to get all comments for many different tasks like displaying them or running some statistics on them or do something else.

When you later want to make a change to how you work with your database, you will have to make the change everywhere.

But this is not the only problem. The way it works now, requires that you controllers know database specific details.

As you can see we will have two more files in the models folder with content looking like the following

vardb=require('../db'),DATABASE='comments'exports.submit=function(user,comment,done){vardata={_id:(newDate().toJSON())+':'+usermessage:comment,}db.save(DATABASE,data,function(err,doc){if(err)returndone('Unable to connect to CouchDB')if(doc.error)returndone('Unable to save the comment')done(null,doc)})}exports.all=function(done){db.all(DATABASE,{},function(err,data){if(err)returndone('Unable to connect to CouchDB')if(data.error)returndone('Unable to get the comments')done(null,data.rows)})}exports.recent=function(done){db.all(DATABASE,{limit:20,descending:true},function(err,data){if(err)returndone('Unable to connect to CouchDB')if(data.error)returndone('Unable to get the comments')done(null,data.rows)})}

Our comment model uses what we already implemented in our database library and hides the implementation from our controllers. It just provides a few useful methods like submit, all and recent which are also easier to understand when you read the controller.

Let’s have a look at how our controller will look like after we introduce the model.

varexpress=require('express'),router=express.Router()varComments=require('../models/comments')// Submit a comment
router.post('/submit',function(req,res){Comments.submit(req.session.user._id,req.body.comment,function(err,doc){res.redirect('/all')})})// Get all comments
router.get('/all',function(req,res){Comments.all(function(err,docs){res.render('comments',{comments:docs})})})// Get most recent comments
router.get('/recent',function(req,res){Comments.recent(function(err,docs){res.render('comments',{comments:docs})})})module.exports=router

At the end our models share the database management file db.js and they are the only ones who are aware how we are requesting our data from CouchDB.

Next

We have just barely scratched the surface of how you can work with CouchDB. For example, we have talked very little on how different errors should be handled and the code above is missing most error handling.

We also didn’t mention anything about using and defining views, one of the core features of CouchDB.

There is much more, but with the base structure from above you should easily work with all other CouchDB features.

Did you like this article?

Please share it

Enter your email and get our NPM Cheat Sheet for NodeJS Developers and
the links to our 5 most popular articles which have helped thousands of
developers build faster, more reliable and easier to maintain Node applications.