CouchDB-API

A Node.JS wrapper for the CouchDB API

client

lib/client.js

Module Dependencies

var _ = require("underscore"),
	http = require('http'),
	url = require('url'),
	qs = require("querystring"),
	crypto = require("crypto"),
	sha1 = crypto.createHash("sha1"),
	cookie = require("./cookie"),
	Vargs = require("vargs").Constructor;
  • class: Client

The Client deals with all the HTTP communication with CouchDB. It will handle content-negotiation and response.

function Client() {}

Performs an HTTP request

  • param: object urlObj Object returned from url.parse()

  • param: object [options] HTTP request configuration

  • param: function callback Callback for HTTP response

Client.prototype.request = function (urlObj, options, callback) {
	var self = this,
		opts = _.clone(options);

	_.defaults(opts, {
		method: "GET",
		headers: {}
	});

	_.defaults(opts.headers, {
		"content-type": "application/json",
		"accept": ["/plain", "application/json", "application/x-www-form-urlencoded"].join(", ")
	});

	opts.method = opts.method.toUpperCase();

	opts.host = urlObj.hostname;
	opts.port = urlObj.port;
	opts.path = urlObj.pathname;
	if (urlObj.query) {
		opts.path += "?" + qs.stringify(urlObj.query);
	}

	if (this.user) {
		opts.headers.authorization = "Basic " + new Buffer(this.user.name + ":" + this.user.password).toString("base64");
	}

	if (this.debug) {
		console.log(opts);
	}

	var req = http.request(opts, function (res) {
		var response = "";

		res.setEncoding('utf8');

		res.on('data', function (chunk) {
			response += chunk;
		});

		res.on('end', function () {
			var result = response;

			switch (res.headers["content-type"]) {
			case "application/json":
				result = JSON.parse(result);
				break;

			case "application/x-www-form-urlencoded":
				result = qs.parse(result);
				break;
			}

			if (res.statusCode >= 200 && res.statusCode < 300) {
				callback.call(self, null, result, res.headers, res.statusCode);
			} else {
				callback.call(self, result, null, res.headers, res.statusCode);
			}

			if (self.debug) {
				console.log(result, res.headers, res.statusCode);
			}
		});
	});

	if (opts.body) {
		var body = opts.body;
		switch (opts.headers["content-type"]) {
		case "application/json":
			body = JSON.stringify(body);
			break;

		case "application/x-www-form-urlencoded":
			body = qs.stringify(body);
			break;
		}
		req.write(body);
	}

	req.end();
};

Performs an HTTP GET request

  • param: object urlObj Object returned from url.parse()

  • param: object [options] HTTP request configuration

  • param: function callback Callback for HTTP response

Client.prototype.get = function () {
	var args = new Vargs(arguments),
		urlObj = args.first,
		options = args.all[1] || {};

	options.method = "GET";

	this.request(urlObj, options, args.callback);
};

Performs an HTTP PUT request

  • param: object urlObj Object returned from url.parse()

  • param: string | object body Body of request (string, json object, qs object, etc.)

  • param: object [options] HTTP request configuration

  • param: function callback Callback for HTTP response

Client.prototype.put = function () {
	var args = new Vargs(arguments),
		urlObj = args.first,
		body = args.all[1] || null,
		options = args.all[2] || {};

	options.method = "PUT";
	if (body) {
		options.body = body;
	}

	this.request(urlObj, options, args.callback);
};

Performs an HTTP POST request

  • param: object urlObj Object returned from url.parse()

  • param: string | object body Body of request (string, json object, qs object, etc.)

  • param: object [options] HTTP request configuration

  • param: function callback Callback for HTTP response

Client.prototype.post = function () {
	var args = new Vargs(arguments),
		urlObj = args.first,
		body = args.all[1] || null,
		options = args.all[2] || {};

	options.method = "POST";
	if (body) {
		options.body = body;
	}

	this.request(urlObj, options, args.callback);
};

Performs an HTTP DELETE request

  • param: object urlObj Object returned from url.parse()

  • param: object [options] HTTP request configuration

  • param: function callback Callback for HTTP response

Client.prototype.del = function () {
	var args = new Vargs(arguments),
		urlObj = args.first,
		options = args.all[1] || {};

	options.method = "DELETE";

	this.request(urlObj, options, args.callback);
};

Export to CommonJS

module.exports = Client;

cookie

lib/cookie.js

HTTP Cookie Toolkit - requires: underscore .js

Module Dependencies

var _ = require("underscore");

Parses the HTTP Header into a Cookie object

  • param: string | string[] The HTTP header to parse

  • return: object

module.exports.parse = function (cookieStr) {
	// If an array is passed, just use map to return an array of objects
	if (_.isArray(cookieStr)) {
		return _.map(cookieStr, this.parse);
	}

	// split up the main components
	var parts = cookieStr.split(";"),
		main = parts.shift().split("="),
		ret = {
			name: main[0],
			value: main[1]
		};

	// look the portions we have and build our object
	_.each(parts, function (part, x) {
		part = part.split("=");

		// here we divide up the part into a key/value pair
		//   (sometimes we'll only have a key as a flag)
		var name = part[0] ? part[0].trim() : "",
			value = part[1] ? part[1].trim() : "";

		// add the key/value pair to our obj
		ret[name] = value;
	});

	return ret;
};

Output an HTTP header based on a parsed Cookie object

  • param: object The parsed Cookie object

  • return: string HTTP header text (set-cookie)

module.exports.format = function (cookieObj) {
	var cookie = _.clone(cookieObj),              // create a clone, rather than working directly with the obj
		ret = [cookie.name + "=" + cookie.value]; // initialize the return array, with the cookie name/value as the first index

	// delete values we no longer need
	delete cookie.name;
	delete cookie.value;

	// add the flags and other configuration options
	_.each(cookie, function (value, key) {
		ret.push(value ? key + "=" + value : key);
	});

	return ret.join("; ");
};

database

lib/database.js

Module Dependencies

var Document = require("./document"),
	DesignDocument = require("./designDocument"),
	_ = require("underscore");
  • class: Database

Handles interaction at the CouchDB database level

function Database(server, name) {
	this.server = server; // add a reference to the server
	this.name = name;

	this.urlObj = _.clone(server.urlObj); // use a copy so we can make changes
	this.urlObj.pathname += name + "/";   // add the db name and trailing slash to the pathname

	this.client = this.server.client; // add a reference to the client
}
  • augments: Database
_.extend(Database.prototype, require("./request"));

Get basic information about the database

GET /db

  • param: function callback Callback to be performed (arguments: err, response)

Database.prototype.info = function (callback) {
	this.req("get", callback);
};

Create the database

PUT /db

  • param: function callback Callback to be performed (arguments: err, response)

Database.prototype.create = function (callback) {
	this.req("put", callback);
};

Drop the database

DELETE /db

  • param: function callback Callback to be performed (arguments: err, response)

Database.prototype.drop = function (callback) {
	this.req("del", callback);
};

Query the CouchDB _changes API

GET /db/_changes

  • param: object query Query arguments to be passed

  • param: function callback Callback to be performed (arguments: err, response)

Database.prototype.changes = function (query, callback) {
	var url = {
		pathname: "_changes",
		query: query
	};
	this.req("get", url, callback);
};

Perform a database (or design document view index) compation

  • GET /db/_compact
  • GET /db/_compact/ddoc

  • param: string [ddoc] If passed, will compact the specified design document's view indexes

  • param: function callback Callback to be performed (arguments: err, response)

Database.prototype.compact = function (ddoc, callback) {
	if (typeof ddoc === "function") {
		callback = ddoc;
		ddoc = null;
	}

	var path = "_compact";
	if (ddoc) {
		path += "/" + ddoc;
	}

	this.req("post", path, callback);
};

Clear the cached view output

GET /db/_view_cleanup

  • param: function callback Callback to be performed (arguments: err, response)

Database.prototype.viewCleanup = function (callback) {
	this.req("post", "_view_cleanup", callback);
};

Commits recent db changes to disk

GET /db/_ensure_full_commit

  • param: function callback Callback to be performed (arguments: err, response)

Database.prototype.ensureFullCommit = function (callback) {
	this.req("post", "_ensure_full_commit", callback);
};

Returns a new Document object

  • param: string [id] The Document's _id

  • return: object A new Document object

Database.prototype.doc = function (id) {
	return new Document(this, id);
};

Returns a new DesignDocument object

  • param: string name The DesignDocument's name

  • return: object A new DesignDocument object

Database.prototype.ddoc = function (name) {
	return new DesignDocument(this, name);
};

Export to CommonJS

module.exports = Database;

designDocument

lib/designDocument.js

Module Dependencies

var _ = require("underscore"),
	Document = require("./document");
  • class: DesignDocument

Handles interaction at the design document level

function DesignDocument(db, name) {
	this.db = db;
	this.server = db.server;
	this.id = "_design/" + name;

	this.urlObj = _.clone(db.urlObj);

	this.body = {
		_id: this.id
	};
	this.urlObj.pathname += this.id;

	this.client = this.server.client;
}
  • augments: Document
_.extend(DesignDocument.prototype, Document.prototype);

Gets the list of views

DesignDocument.prototype.views = function () {
	this.body.views = this.body.views || {};
	return this.body.views;
};

Add a new view to this design document

  • param: function map Map function for this view

  • param: function | string [reduce] Reduce function for this view

DesignDocument.prototype.view = function (name, map, reduce) {
	if (map) {
		var view = { map: map.toString() };
		if (reduce) {
			view.reduce = reduce.toString();
		}
		this.views()[name] = view;
	} else {
		return this.views()[name];
	}
};

Get all the show functions

DesignDocument.prototype.shows = function () {
	this.body.shows = this.body.shows || {};
	return this.body.shows;
};

Get/set the specified show function

DesignDocument.prototype.show = function (name, func) {
	if (func) {
		this.shows()[name] = func.toString();
	} else {
		return this.shows()[name];
	}
};

Get all the list functions

DesignDocument.prototype.lists = function () {
	this.body.lists = this.body.lists || {};
	return this.body.lists;
};

Get/set the specified list function

DesignDocument.prototype.list = function (name, func) {
	if (func) {
		this.lists()[name] = func.toString();
	} else {
		return this.lists()[name];
	}
};

Get all the update handler

DesignDocument.prototype.updates = function () {
	this.body.updates = this.body.updates || {};
	return this.body.updates;
};

Get/set the specified update handler

DesignDocument.prototype.update = function (name, func) {
	if (func) {
		this.updates()[name] = func.toString();
	} else {
		return this.updates()[name];
	}
};

Get/set the validation function

DesignDocument.prototype.val = function (func) {
	if (func) {
		this.body.validate_doc_update = func.toString();
	} else {
		return this.body.validate_doc_update;
	}
};

Export to CommonJS

module.exports = DesignDocument;

document

lib/document.js

Module Dependencies

var Client = require("./client"),
	_ = require("underscore");
  • class: Document

Handles interaction at the document level

function Document(db, id) {
	this.db = db;            // create a reference to the database object
	this.server = db.server; // create a reference to the server object
	this.id = id || null;    // default: null (CouchDB can generate an `_id` for us)

	this.urlObj = _.clone(db.urlObj); // create a copy, so we can modify for this doc

	this.body = {}; // initialize the document body
	if (id) {
		// if there is an id, initialize where needed
		this.body._id = id;
		this.urlObj.pathname += id;
	}

	this.client = this.server.client; // create a reference to HTTP client
}
  • augments: Document
_.extend(Document.prototype, require("./request"));

Retrieve the document from the server

GET /db/doc

  • param: function callback Callback to be performed (arguments: err, doc)

Document.prototype.get = function (callback) {
	var self = this;
	this.req("get", null, function (err, response) {
		if (!err) {
			self.body = response;
		}

		callback.apply(self, arguments);
	});
};

Save the document's current state to the server

  • PUT /db/doc (w/ id)
  • POST /db (w/o id)

  • param: function callback Callback to be performed (arguments: err, doc)

Document.prototype.save = function (callback) {
	var self = this;

	this.req(this.id ? "put" : "post", null, this.body, function (err, response) {
		if (!err) {
			if (!self.id) {
				self.id = self.body._id = response.id;
				self.urlObj.pathname += response.id;
			}
			self.body._rev = response.rev;
		}

		callback.apply(self, arguments);
	});
};

Delete the document from the server

DELETE /db/doc

  • param: function callback Callback to be performed (arguments: err, doc)

Document.prototype.del = function (callback) {
	var self = this,
		options = { headers: { "if-match": this.body._rev } }; // use "If-Match" instead of URL param

	this.req("del", null, options, function (err, response) {
		if (!err) {
			self.body = false;
		}

		callback.apply(self, arguments);
	});
};

Export to CommonJS

module.exports = Document;

request

lib/request.js

Module Dependencies

var _ = require("underscore"),
	url = require("url");

Pass along a request to the underlying Client object, using the current objects urlObj as the base

exports.req = function () {
	var args = _.toArray(arguments),
		method = args.shift().toLowerCase();

	if (args.length > 1) {
		args[0] = modifyUrl(this.urlObj, args[0]);
	} else {
		args.unshift(this.urlObj);
	}

	this.client[method].apply(this.client, args);
};

Enable debug mode for the client object (not very useful externally right now)

  • param: bool [status] If passed, will use this to set the flag, otherwise it defaults to true

exports.debug = function (status) {
	this.client.debug = typeof status === "undefined" ? true : !!status;
};

server

lib/server.js

Module Dependencies

var Client = require("./client"),
	Database = require("./database"),
	qs = require("querystring"),
	url = require("url"),
	_ = require("underscore");
  • class: Document

Handles interaction at the server level

function Server(host, port) {
	// use this as the basis for our urlObj
	var urlObj = url.parse("http://localhost:5984/");

	// if host provided, augment the urlObj
	if (host) {
		urlObj.host = host;
	}

	// if port provided, augment the urlObj
	if (port) {
		urlObj.port = port;
	}

	this.urlObj = urlObj; // add a public reference

	this.client = new Client(); // create a client for each server
}
  • augments: Server
_.extend(Server.prototype, require("./request"));

Get basic information about the server

GET /

  • param: function callback Callback to be performed (arguments: err, response)

Server.prototype.info = function (callback) {
	this.req("get", callback);
};

Get a list of all the databases on the server

GET /_all_dbs

  • param: function callback Callback to be performed (arguments: err, dbs)

Server.prototype.allDbs = function (callback) {
	this.req("get", "_all_dbs", callback);
};

Get information about all the active tasks currently running on the server

GET /_active_tasks

  • param: function callback Callback to be performed (arguments: err, tasks)

Server.prototype.activeTasks = function (callback) {
	this.req("get", "_active_tasks", callback);
};

Get the text of the server's log file

GET /_log

  • param: object query Query arguments to be passed

  • param: function callback Callback to be performed (arguments: err, response)

Server.prototype.log = function (query, callback) {
	if (typeof query === "function") {
		callback = query;
		query = null;
	}

	if (!query) {
		this.req("get", "_log", callback);
	} else {
		this.req("get", {
			pathname: "_log",
			query: query
		}, callback);
	}
};

Restarts the server process

GET /_restart

  • param: function callback Callback to be performed (arguments: err, response)

Server.prototype.restart = function (callback) {
	this.req("post", "_restart", callback);
};

Restarts the server process

GET /_restart

  • param: function callback Callback to be performed (arguments: err, response)

Server.prototype.stats = function (callback) {
	this.req("get", "_stats", callback);
};

Get a list of generated UUIDs from the server

GET /_uuids

  • param: number [count] Number of UUIDs to be returned (default: 1)

  • param: function callback Callback to be performed (arguments: err, uuids)

Server.prototype.uuids = function (count, callback) {
	var self = this;

	if (typeof count === "function") {
		callback = count;
		count = 1;
	}

	this.req("get", {
		pathname: "_uuids",
		query: { count: count || 5 }
	}, function (err, response) {
		callback.call(self, null, response.uuids);
	});
};

Specify a username/password to send to the server with each request (uses HTTP Basic auth)

  • param: string user Username (either server admin or _users user)

  • param: string pass Password

Server.prototype.setUser = function (user, pass) {
	this.client.user = {
		name:     user,
		password: pass
	};
};

Attempt a login against the server itself

  • param: string user Username

  • param: string pass Password

  • param: function callback Callback to be performed (arguments: err, response)

Server.prototype.login = function (user, pass, callback) {
	var self = this, body, options;

	body = {
		name: user,
		password: pass
	};

	options = {
		headers: {
			"content-type": "application/x-www-form-urlencoded"
		}
	};

	this.req("post", "_session", body, options, function (err, response) {
		if (!err) {
			self.client.user = {
				ctx:      _.clone(response),
				name:     user,
				password: pass
			};
		}

		callback.apply(self, arguments);
	});
};

Logout (destroy the session)

  • param: function callback Callback to be performed (arguments: err, response)

Server.prototype.logout = function (callback) {
	this.req("del", "_session", callback);
};

Check the status of the current session

  • param: function callback Callback to be performed (arguments: err, response)

Server.prototype.session = function (callback) {
	this.req("get", "_session", callback);
};

Returns a new Database object

  • param: string name The Database name

  • return: object A new Database object

Server.prototype.db = function (name) {
	return new Database(this, name);
};

Export to CommonJS

module.exports = Server;