Code Monkey home page Code Monkey logo

nano's Introduction

ByChatTestsCoverageDependenciesNPM

nano

minimalistic couchdb driver for node.js

nano features:

  • minimalistic - there is only a minimum of abstraction between you and couchdb
  • pipes - proxy requests from couchdb directly to your end user
  • errors - errors are proxied directly from couchdb: if you know couchdb you already know nano.

installation

  1. install npm
  2. npm install nano

table of contents

getting started

to use nano you need to connect it to your couchdb install, to do that:

var nano = require('nano')('http://localhost:5984');

to create a new database:

nano.db.create('alice');

and to use it:

var alice = nano.db.use('alice');

in this examples we didn't specify a callback function, the absence of a callback means "do this, ignore what happens". in nano the callback function receives always three arguments:

  • err - the error, if any
  • body - the http response body from couchdb, if no error. json parsed body, binary for non json responses
  • header - the http response header from couchdb, if no error

a simple but complete example using callbacks is:

var nano = require('nano')('http://localhost:5984');

// clean up the database we created previously
nano.db.destroy('alice', function() {
  // create a new database
  nano.db.create('alice', function() {
    // specify the database we are going to use
    var alice = nano.use('alice');
    // and insert a document in it
    alice.insert({ crazy: true }, 'rabbit', function(err, body, header) {
      if (err) {
        console.log('[alice.insert] ', err.message);
        return;
      }
      console.log('you have inserted the rabbit.')
      console.log(body);
    });
  });
});

if you run this example(after starting couchdb) you will see:

you have inserted the rabbit.
{ ok: true,
  id: 'rabbit',
  rev: '1-6e4cb465d49c0368ac3946506d26335d' }

you can also see your document in futon.

configuration

configuring nano to use your database server is as simple as:

var nano   = require('nano')('http://localhost:5984')
  , db     = nano.use('foo')
  ;

however if you don't need to instrument database objects you can simply:

// nano parses the url and knows this is a database
var db = require('nano')('http://localhost:5984/foo');

you can also pass options to the require:

// nano parses the url and knows this is a database
var db = require('nano')('http://localhost:5984/foo');

to specify further configuration options you can pass an object literal instead:

// nano parses the url and knows this is a database
var db = require('nano')(
  { "url"             : "http://localhost:5984/foo"
  , "requestDefaults" : { "proxy" : "http://someproxy" }
  , "log"             : function (id, args) {
      console.log(id, args);
    }
  });

Please check request for more information on the defaults. They support features like cookie jar, proxies, ssl, etc.

You can tell nano to not parse the url (maybe the server is behind a proxy, is accessed through a rewrite rule or other):

// nano does not parse the url and return the server api
// "http://localhost:5984/prefix" is the CouchDB server root
var couch = require('nano')(
  { "url"      : "http://localhost:5984/prefix"
    "parseUrl" : false
  });
var db = couch.use('foo');

pool size and open sockets

a very important configuration parameter if you have a high traffic website and are using nano is setting up the pool.size. by default, the node.js http global agent (client) has a certain size of active connections that can run simultaneously, while others are kept in a queue. pooling can be disabled by setting the agent property in requestDefaults to false, or adjust the global pool size using:

http.globalAgent.maxSockets = 20;

you can also increase the size in your calling context using requestDefaults if this is problematic. refer to the request documentation and examples for further clarification.

here's an example explicitly using the keep alive agent (installed using npm install agentkeepalive), especially useful to limit your open sockets when doing high-volume access to couchdb on localhost:

var agentkeepalive = require('agentkeepalive');
var myagent = new agentkeepalive({
    maxSockets: 50
  , maxKeepAliveRequests: 0
  , maxKeepAliveTime: 30000
  });

var db = require('nano')(
  { "url"              : "http://localhost:5984/foo"
  , "requestDefaults" : { "agent" : myagent }
  });

database functions

nano.db.create(name, [callback])

creates a couchdb database with the given name.

nano.db.create('alice', function(err, body) {
  if (!err) {
    console.log('database alice created!');
  }
});

nano.db.get(name, [callback])

get informations about name.

nano.db.get('alice', function(err, body) {
  if (!err) {
    console.log(body);
  }
});

nano.db.destroy(name, [callback])

destroys name.

nano.db.destroy('alice');

even though this examples looks sync it is an async function.

nano.db.list([callback])

lists all the databases in couchdb

nano.db.list(function(err, body) {
  // body is an array
  body.forEach(function(db) {
    console.log(db);
  });
});

nano.db.compact(name, [designname], [callback])

compacts name, if designname is specified also compacts its views.

nano.db.replicate(source, target, [opts], [callback])

replicates source on target with options opts. target has to exist, add create_target:true to opts to create it prior to replication.

nano.db.replicate('alice', 'http://admin:[email protected]:5984/alice',
                  { create_target:true }, function(err, body) {
    if (!err)
      console.log(body);
});

nano.db.changes(name, [params], [callback])

asks for the changes feed of name, params contains additions to the query string.

nano.db.changes('alice', function(err, body) {
  if (!err) {
    console.log(body);
  }
});

nano.db.follow(name, [params], [callback])

Uses Follow to create a solid changes feed. please consult follow documentation for more information as this is a very complete API on it's own.

var feed = db.follow({since: "now"});
feed.on('change', function (change) {
  console.log("change: ", change);
});
feed.follow();
process.nextTick(function () {
  db.insert({"bar": "baz"}, "bar");
});

nano.db.info([callback])

gets database information.

nano.db.info(function(err, body) { if (!err) { console.log('got database info'', body); } });

nano.use(name)

creates a scope where you operate inside name.

var alice = nano.use('alice');
alice.insert({ crazy: true }, 'rabbit', function(err, body) {
  // do something
});

nano.db.use(name)

alias for nano.use

nano.db.scope(name)

alias for nano.use

nano.scope(name)

alias for nano.use

nano.request(opts, [callback])

makes a request to couchdb, the available opts are:

  • opts.db – the database name
  • opts.method – the http method, defaults to get
  • opts.path – the full path of the request, overrides opts.doc and opts.att
  • opts.doc – the document name
  • opts.att – the attachment name
  • opts.qs – query string parameters, appended after any existing opts.path, opts.doc, or opts.att
  • opts.content_type – the content type of the request, default to json
  • opts.headers – additional http headers, overrides existing ones
  • opts.body – the document or attachment body
  • opts.encoding – the encoding for attachments
  • opts.multipart – array of objects for multipart request

nano.relax(opts, [callback])

alias for nano.request

nano.dinosaur(opts, [callback])

alias for nano.request

                _
              / '_)  WAT U SAY!
     _.----._/  /
    /          /
  _/  (   | ( |
 /__.-|_|--|_l

nano.config

an object containing the nano configurations, possible keys are:

  • url - the couchdb url
  • db - the database name

nano.updates([params], [callback])

listen to db updates, the available params are:

  • params.feed – Type of feed. Can be one of
  • longpoll: Closes the connection after the first event.
  • continuous: Send a line of JSON per event. Keeps the socket open until timeout.
  • eventsource: Like, continuous, but sends the events in EventSource format.
  • params.timeout – Number of seconds until CouchDB closes the connection. Default is 60.
  • params.heartbeat – Whether CouchDB will send a newline character (\n) on timeout. Default is true.

nano.followUpdates([params], [callback])

** changed in version 6 **

Use Follow to create a solid _db_updates feed. Please consult follow documentation for more information as this is a very complete api on it's own

var feed = nano.followUpdates({since: "now"});
feed.on('change', function (change) {
  console.log("change: ", change);
});
feed.follow();
process.nextTick(function () {
  nano.db.create('alice');
});

document functions

db.insert(doc, [params], [callback])

inserts doc in the database with optional params. if params is a string, its assumed as the intended document name. if params is an object, its passed as query string parameters and docName is checked for defining the document name.

var alice = nano.use('alice');
alice.insert({ crazy: true }, 'rabbit', function(err, body) {
  if (!err)
    console.log(body);
});

The insert function can also be used with the method signature db.insert(doc,[callback]), where the doc contains the _id field e.g.

var alice = nano.use('alice')
alice.insert({ _id: 'myid', crazy: true }, function(err, body) {
  if (!err)
    console.log(body)
})

and also used to update an existing document, by including the _rev token in the document being saved:

var alice = nano.use('alice')
alice.insert({ _id: 'myid', _rev: '1-23202479633c2b380f79507a776743d5', crazy: false }, function(err, body) {
  if (!err)
    console.log(body)
})

db.destroy(docname, rev, [callback])

removes revision rev of docname from couchdb.

alice.destroy('rabbit', '3-66c01cdf99e84c83a9b3fe65b88db8c0', function(err, body) {
  if (!err)
    console.log(body);
});

db.get(docname, [params], [callback])

gets docname from the database with optional query string additions params.

alice.get('rabbit', { revs_info: true }, function(err, body) {
  if (!err)
    console.log(body);
});

db.head(docname, [callback])

same as get but lightweight version that returns headers only.

alice.head('rabbit', function(err, _, headers) {
  if (!err)
    console.log(headers);
});

db.copy(src_doc, dest_doc, opts, [callback])

copy the contents (and attachments) of a document to a new document, or overwrite an existing target document

alice.copy('rabbit', 'rabbit2', { overwrite: true }, function(err, _, headers) {
  if (!err)
    console.log(headers);
});

db.bulk(docs, [params], [callback])

bulk operations(update/delete/insert) on the database, refer to the couchdb doc.

db.list([params], [callback])

list all the docs in the database with optional query string additions params. This is useful for searching.

alice.list({startkey:'cat', limit:3}, function(err, body) {
  if (!err) {
    body.rows.forEach(function(doc) {
      console.log(doc);
    });
  }
});

For a full list of params, see couchdb doc.

db.fetch(docnames, [params], [callback])

bulk fetch of the database documents, docnames are specified as per couchdb doc. additional query string params can be specified, include_docs is always set to true.

db.fetchRevs(docnames, [params], [callback])

** changed in version 6 **

bulk fetch of the revisions of the database documents, docnames are specified as per couchdb doc. additional query string params can be specified, this is the same method as fetch but include_docs is not automatically set to true.

multipart functions

db.multipart.insert(doc, attachments, params, [callback])

inserts a doc together with attachments and params. if params is a string, its assumed as the intended document name. if params is an object, its passed as query string parameters and docName is checked for defining the document name. refer to the doc for more details. attachments must be an array of objects with name, data and content_type properties.

var fs = require('fs');

fs.readFile('rabbit.png', function(err, data) {
  if (!err) {
    alice.multipart.insert({ foo: 'bar' }, [{name: 'rabbit.png', data: data, content_type: 'image/png'}], 'mydoc', function(err, body) {
        if (!err)
          console.log(body);
    });
  }
});

db.multipart.get(docname, [params], [callback])

get docname together with its attachments via multipart/related request with optional query string additions params. refer to the doc for more details. the multipart response body is a Buffer.

alice.multipart.get('rabbit', function(err, buffer) {
  if (!err)
    console.log(buffer.toString());
});

attachments functions

db.attachment.insert(docname, attname, att, contenttype, [params], [callback])

inserts an attachment attname to docname, in most cases params.rev is required. refer to the doc for more details.

var fs = require('fs');

fs.readFile('rabbit.png', function(err, data) {
  if (!err) {
    alice.attachment.insert('rabbit', 'rabbit.png', data, 'image/png',
      { rev: '12-150985a725ec88be471921a54ce91452' }, function(err, body) {
        if (!err)
          console.log(body);
    });
  }
});

or using pipe:

var fs = require('fs');

fs.createReadStream('rabbit.png').pipe(
    alice.attachment.insert('new', 'rab.png', null, 'image/png')
);

db.attachment.get(docname, attname, [params], [callback])

get docname's attachment attname with optional query string additions params.

var fs = require('fs');

alice.attachment.get('rabbit', 'rabbit.png', function(err, body) {
  if (!err) {
    fs.writeFile('rabbit.png', body);
  }
});

or using pipe:

var fs = require('fs');

alice.attachment.get('rabbit', 'rabbit.png').pipe(fs.createWriteStream('rabbit.png'));

db.attachment.destroy(docname, attname, [params], [callback])

changed in version 6

destroy attachment attname of docname's revision rev.

alice.attachment.destroy('rabbit', 'rabbit.png',
    {rev: '1-4701d73a08ce5c2f2983bf7c9ffd3320'}, function(err, body) {
      if (!err)
        console.log(body);
});

views and design functions

db.view(designname, viewname, [params], [callback])

calls a view of the specified design with optional query string additions params. if you're looking to filter the view results by key(s) pass an array of keys, e.g { keys: ['key1', 'key2', 'key_n'] }, as params.

alice.view('characters', 'crazy_ones', function(err, body) {
  if (!err) {
    body.rows.forEach(function(doc) {
      console.log(doc.value);
    });
  }
});

db.viewWithList(designname, viewname, listname, [params], [callback])

calls a list function feeded by the given view of the specified design document.

alice.viewWithList('characters', 'crazy_ones', 'my_list', function(err, body) {
  if (!err) {
    console.log(body);
  }
});

db.show(designname, showname, doc_id, [params], [callback])

calls a show function of the specified design for the document specified by doc_id with optional query string additions params.

alice.show('characters', 'format_doc', '3621898430', function(err, doc) {
  if (!err) {
    console.log(doc);
  }
});

take a look at the couchdb wiki for possible query paramaters and more information on show functions.

db.atomic(designname, updatename, docname, [body], [callback])

calls the design's update function with the specified doc in input.

db.atomic("update", "inplace", "foobar",
{field: "foo", value: "bar"}, function (error, response) {
  assert.equal(error, undefined, "failed to update");
  assert.equal(response.foo, "bar", "update worked");
});

Note that the data is sent in the body of the request. An example update handler follows:

"updates": {
  "in-place" : "function(doc, req) {
      var field = req.body.field;
      var value = req.body.value;
      var message = 'set '+field+' to '+value;
      doc[field] = value;
      return [doc, message];
  }"

db.search(designname, searchname, [params], [callback])

calls a view of the specified design with optional query string additions params.

alice.search('characters', 'crazy_ones', { q: 'cat' }, function(err, doc) {
  if (!err) {
    console.log(doc);
  }
});

check out the tests for a fully functioning example.

using cookie authentication

nano supports making requests using couchdb's cookie authentication functionality. there's a example in coffeescript, but essentially you just:

var nano     = require('nano')('http://localhost:5984')
  , username = 'user'
  , userpass = 'pass'
  , callback = console.log // this would normally be some callback
  , cookies  = {} // store cookies, normally redis or something
  ;

nano.auth(username, userpass, function (err, body, headers) {
  if (err) {
    return callback(err);
  }

  if (headers && headers['set-cookie']) {
    cookies[user] = headers['set-cookie'];
  }

  callback(null, "it worked");
});

reusing a cookie:

var auth = "some stored cookie"
  , callback = console.log // this would normally be some callback
  , alice = require('nano')(
    { url : 'http://localhost:5984/alice', cookie: 'AuthSession=' + auth });
  ;

alice.insert(doc, function (err, body, headers) {
  if (err) {
    return callback(err);
  }

  // change the cookie if couchdb tells us to
  if (headers && headers['set-cookie']) {
    auth = headers['set-cookie'];
  }

  callback(null, "it worked");
});

getting current session:

var nano = require('nano')({url: 'http://localhost:5984', cookie: 'AuthSession=' + auth});

nano.session(function(err, session) {
  if (err) {
    return console.log('oh noes!')
  }

  console.log('user is %s and has these roles: %j',
    session.userCtx.name, session.userCtx.roles);
});

advanced features

extending nano

nano is minimalistic but you can add your own features with nano.request(opts, callback)

for example, to create a function to retrieve a specific revision of the rabbit document:

function getrabbitrev(rev, callback) {
  nano.request({ db: 'alice',
                 doc: 'rabbit',
                 method: 'get',
                 params: { rev: rev }
               }, callback);
}

getrabbitrev('4-2e6cdc4c7e26b745c2881a24e0eeece2', function(err, body) {
  if (!err) {
    console.log(body);
  }
});

pipes

you can pipe in nano like in any other stream. for example if our rabbit document has an attachment with name picture.png (with a picture of our white rabbit, of course!) you can pipe it to a writable stream

var fs = require('fs'),
    nano = require('nano')('http://127.0.0.1:5984/');
var alice = nano.use('alice');
alice.attachment.get('rabbit', 'picture.png').pipe(fs.createWriteStream('/tmp/rabbit.png'));

then open /tmp/rabbit.png and you will see the rabbit picture.

tutorials, examples in the wild & screencasts

roadmap

check issues

tests

to run (and configure) the test suite simply:

cd nano
npm install
npm test

after adding a new test you can run it individually (with verbose output) using:

nano_env=testing node tests/doc/list.js list_doc_params

where list_doc_params is the test name.

meta

                _
              / _) roar! i'm a vegan!
       .-^^^-/ /
    __/       /
   /__.|_|-|_|     cannes est superb

(oo)--',- in caos

license

copyright 2011 nuno job <nunojob.com> (oo)--',--

licensed under the apache license, version 2.0 (the "license"); you may not use this file except in compliance with the license. you may obtain a copy of the license at

http://www.apache.org/licenses/LICENSE-2.0.html

unless required by applicable law or agreed to in writing, software distributed under the license is distributed on an "as is" basis, without warranties or conditions of any kind, either express or implied. see the license for the specific language governing permissions and limitations under the license.

nano's People

Contributors

alesch avatar ameensol avatar chesles avatar cliffano avatar cttttt avatar dscape avatar fracek avatar glynnbird avatar gr2m avatar jandrieu avatar jaybeavers avatar jhs avatar jlank avatar jo avatar klaemo avatar mark-hahn avatar mmalecki avatar oleics avatar perezd avatar pgte avatar pmanijak avatar rapidrapids avatar revington avatar satazor avatar shtaft avatar streunerlein avatar suprememoocow avatar svnlto avatar tarantoga avatar thiagoarrais avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

nano's Issues

passing an array to updateWithHandler

When I am trying to pass an array as an argument with updateWithHandler the couchdb update function is only being passed the last argument of the array.

For example when I do this:

db.updateWithHandler('test','take_array', "some_id", {"my_array": ["abc","123"]}, function(){ ... })

in the update function req.query.my_array == "123"

I'm guessing there is an issue when converting the JSON object of arguments into a query string

update a document

Hi, I'm new to couch, so I may miss something. I'm looking for a way to update an existing document but can't find that in the API, is this something I'm supposed to write myself?

errs not in dependencies

Package.json has errs as dev dependency instead of dependency. Causes the following error on require('nano'):

require('nano')
Error: Cannot find module 'errs'
at Function._resolveFilename (module.js:332:11)
at Function._load (module.js:279:25)
at Module.require (module.js:354:17)
at require (module.js:370:17)
at Object. (/Volumes/MacHD2/verticals/cs-whereis-poi-extract/node_modules/nano/nano.js:21:19)
at Module._compile (module.js:441:26)
at Object..js (module.js:459:10)
at Module.load (module.js:348:31)
at Function._load (module.js:308:12)
at Module.require (module.js:354:17)

Change callback parameter order

Just throwing an idea out there for consideration...

What if the order of the callback parameters was err, body, headers? IMHO opinion, the headers are a little less often needed than the body, aren't they? If the positions were changed, writing a callback that uses the body but not the headers would be function (err, body) instead of function(err, _, body). I find the former to be a lot more pleasant to read.

I realise that this change breaks backward compatibility, but that shouldn't be too big of an issue since we didn't hit 1.0 yet.

Anyway, maybe it's just me. Maybe my use case isn't shared with many others and this entry may be used as some kind of survey.

Proposal: Authentication cookie support

We're currently building an app for which we use a 1 database to 1 user architecture. Along with this we're using CouchDB's cookie authentication mechanism.

I'd like to submit a pull request that will allow the user to supply cookie as part of params. If this is set then the two required headers (X-CouchDB-WWW-Authenticate and cookie) will be added to the request in the relax function.

As per the contribute section of the README, I'm submitting this here first to get views/acceptance on the proposal before submitting a pull request.

Sample usage:

alice.get('rabbit', { revs_info: true, cookie: req.cookies['cookiename'] }, function(err, body) {
   // Pass-through cookie if couchdb has generated a new value
   if (headers && headers['set-cookie'])
     res.cookie('cookiename', headers['set-cookie'], { httpOnly: true });
   if (!err)
     console.log(body);
});

Not Saving/Inserting?

Okay, so I'm using couchdb + node.js to make a bot in a pet capacity.
i.e., you an use text commands to level it up, teach it tricks, and so on and so forth.
Or at least that's the idea, as I can't seem to get couchdb to work. I'm not a big
fan of the RAM useage of MySQL, and mongo ticks me off to no end, but couch
seems relatively easy.

I've tried felix-couchdb, and cradle, but nano works best for me.

Basically, through calling other functions, vars change. Primarily 'exp' and 'level'.
However, when these change, and mSave(); (the function to insert, see below)
is called, the changes aren't noted. Let's say it saves with Level:2 and Exp:50.
When I restart the node process, and it gets the information, it has the default value of 0.

global.mDBName = 'pet2';
global.mHunger = 100;
global.mExp = 0;
global.mLevel = 0;
global.mName = 'BotOne';
global.mType = 1;
global.mUserId = '4fb1c582aaa5cd1b4900001e';

Log("Connecting to couchdb");
global.nano = require('nano')('http://localhost:5984');

Log("Finding database");
nano.db.create(mDBName, function(a) { a ? Log("db found, connecting") : Log("db not found, creating")
});

global.mSave = function() {
  store.insert({name:mName, type:mType, exp:mExp, hunger:mHunger, level:mLevel}, mUserId, function(a, b) {
    a || console.log(a)
  })
  Log('Bot Saved')
};

store.get(mUserId, function(b, a) {
    if(b && "not_found" == b.error) {
      mSave(), Log("Doc not found, creating")
    }else {
      if(b) {
        return console.log(b)
      }
      Log("Connected to doc: name:" + a.name + ", type:" + a.type + ", level:" + a.level + ", exp:" + a.exp + ", hunger:" + a.hunger);
      mHunger = a.hunger;
      mExp = a.exp;
      mLevel = a.level
    }
  });

[debug] hot swappable logging for verbose mode

Verbose mode can be incredibly useful for debugging even in production, instead of just throwing console.log all over the place, we could allow the caller to specify their own custom function for capturing the logs, if a user provides a transport, always pipe to it, otherwise if you're in testing mode, just have that function be console.log

Thoughts?

Couch returning 409's

I've read through the documentation, and I may be doing this wrong, but when I try to insert an image, from file, into a currently existing document and I do:

      fs.createReadStream(doc.image.path).pipe(
                      db.attachment.insert(doc._id, doc.image.name, {}, doc.image.mime)                        
     );

I get back a 409 from couchdb, am I implementing this incorrectly?

[nano] Implement Support for Document Update Handlers

Hello,

I would like to implement support for CouchDb Document Update Handlers.

http://wiki.apache.org/couchdb/Document_Update_Handlers

My thoughts are implementing a new function called update_doc:

   /*
    * calls document update handler
    *
    * @param {design_name:string} design document namd
    * @param {update_name:string} update method to call
    * @param {doc_name:string} document name to update
    * @param {params:object} additions to the querystring
   */   
  function update_doc(design_name, update_name, doc_name, params, callback) {
     if(typeof params === "function") {
       callback = params;
       params = {};
     }
     var update_path = '_design/' + design_name + '/_update/' + update_name + '/' + doc_name;
     return relax({db: db_name, path: update_path, method: "PUT", params: params}, callback);
   }

Then a public db alias called "update"

  public_functions.update = update_doc;

usage

This should provide the ability to easily execute atomic updates on the couchdb server:

Document Update Handler in Design Document called my_design_doc:

"updates": {
  "in-place" : "function(doc, req) {
      var field = req.query.field;
      var value = req.query.value;
      var message = 'set '+field+' to '+value;
      doc[field] = value;
      return [doc, message];
  }"
}

Then using nano:

@db.update("my_design_doc", "in-place", "<doc_name>", { field: "foo", value: "bar" }, function(e,b) { console.log(b); }); 

Thoughts?

[nano] Transaction support?

Have you seen the library for CouchDB transactions I released yesterday, Txn?

The idea is, instead of having a document and trying to store it, you have an id and you first fetch, then see what it looks like, make some changes, and finally store again. (With automatic retry features.) However, the goal is that your code looks roughly the same.

Do you think it would make sense for Nano to expose this functionality? If so, what do you think the API would look like?

Support params on bulk_docs

I need to use _all_docs with the 'include_docs: true' parameter in order to fetch multiple documents in one call. I've tested in my app and I'm getting an immense performance improvement (e.g. from 5 mins down to ~15 seconds) when retrieving documents in batches of 100 at a time from a set of ~3000 documents. I cannot do this in nano today because bulk_docs does not take params. I see two potential solutions: minimal impact but breaking API change or add a new function.

Minimal: function bulk_docs(docs,callback) -> function bulk_docs(docs, params, callback)

New Function: function get_docs(doc_names, params, callback)

thoughts?

Use of request's global cookie `jar`

Sorry to keep firing issues at you - don't worry, I'll do a pull request for this if you agree its a problem. :)


request uses a global cookie jar, into which all response cookies are stored, and then used on subsequent requests. This behaviour is enabled by default; you have to set jar to false to disable it.

I believe that nano should always default for jar to false.

Because of the nature of nano and CouchDB, it is likely that you will doing multiple nano requests as different users (especially if you have a 1 user to 1 database set up) within a single method.

This probably hasn't been noticed before as nano hasn't had support for CouchDB's cookie authentication. But with this coming (#71), it is especially problematic having jar enabled by default - it means that if you have performed an action as a logged in standard user (cookie auth), and then attempt something as admin (using basic auth) - the admin request will fail - because the cookie jar sends over the auth cookie of the standard user - irrespective of whether you have isolated nano instances (because request's cookie jar is global).

See this gist for an example: https://gist.github.com/2717116


It should just be a case of changing this code from:

if (opts.jar) { 
  req.jar = opts.jar;
}

to

req.jar = opts.jar || false;

The interim workaround is to do the above, by using request_defaults (as below), but as I see no benefit for nano in using a cookie jar in any scenario, I think the fix above should be baked in instead.

var nano = require('nano')({ url: 'http://localhost:5984', request_defaults: { jar: false }});

[auth] inconsistency

The way it is implemented gives inconsistent results in the moment auth is called with one of the arguments empty and when both arguments are filled in but user can't be authenticated. In the first case we get back SUCCESS and in the second we get proper result with ERROR with username or password incorrect message. In my opinion this should return always error. The issue here is this line in relax: if(cfg.user && cfg.pass)

Additionally if we have auth we also should have corresponding session deleting function (unauth?). Even if it is very easy to achieve with relax it would be more consistent to have it separately or maybe use auth with empty params instead.

if you agree on this I can do necessary changes.

cheers,
Artur

incorrect failure condition in latest code

HEAD requests have empty bodies, seen below:

>>
{ method: 'HEAD',
  headers: 
   { 'content-type': 'application/json',
     accept: 'application/json' },
  uri: 'http://admin:admin@localhost:5984/tests-master-profile-mappings/pm-d373c6bf6346cd8da895d3ec0f73cc92' }
<<
{ err: 'couch',
  body: undefined,
  headers: 
   { date: 'Mon, 02 Jan 2012 23:30:34 GMT',
     'content-type': 'application/json',
     'cache-control': 'must-revalidate',
     'status-code': 404 } }

I receive the following error:

   uncaught undefined: TypeError: Cannot read property 'reason' of undefined
    at Request._callback (/node_modules/nano/nano.js:167:38)
    at Request.callback (/node_modules/nano/node_modules/request/main.js:99:22)
    at Request.<anonymous> (/node_modules/nano/node_modules/request/main.js:361:18)
    at Request.emit (events.js:64:17)
    at IncomingMessage.<anonymous> (/node_modules/nano/node_modules/request/main.js:327:16)
    at IncomingMessage.emit (events.js:81:20)
    at HTTPParser.onMessageComplete (http.js:133:23)
    at Socket.ondata (http.js:1231:22)
    at Socket._onReadable (net.js:683:27)
    at IOWatcher.onReadable [as callback] (net.js:177:10)

I believe this code is trying to signal an error, however, in the case of a HEAD request, this should not be relying on the existence of a body, since by definition, a HEAD request will only return headers in CouchDB. Thoughts?

[nano] replication needs cancellation/options support

http://wiki.apache.org/couchdb/Replication#Cancelling_a_continuous_replication_task

rather than explicitly supporting continuous as the only option, what we really need is a way to pass any options to the request body, cancel, continuous, doc_ids, are examples of legitimate attributes for replication requests. also, filters and query_params for filtered replication, we need support for all of that.

I'm happy to contribute this, not sure what your opinions are on design/backwards compatibility.

nano URL parsing

In nano 1.3.2, the port number gets junked:

nano('http://127.0.0.1:5984/db')  =>  config: { url: 'http://127.0.0.1', db: 'db' }

A simple rewrite would be:

cfg.url = u.format({protocol:path.protocol,host:path.host})

While I'm at it I'd like to suggest to do

db = path_array.pop()
cfg.url = u.format({protocol:path.protocol,host:path.host,pathname:path_array.join('/')})

so that the path doesn't get junked as well:

nano('http://127.0.0.1:5984/path/to/db')  =>    config: { url: 'http://127.0.0.1:5984/path/to', db: 'db' }

[readme] Errors/Incomplete documentation

Nowhere did you mention that the callback format is

(err, body, headers)

You also don't mention whether body is a string or whether you json parsed it internally.

Oh and then your examples are (err, response, headers) ¬_¬

Turns out from the source that the second argument is a JSON parsed body if it's not binary.

[nano] Follow support?

Hi, Nuno.

Is it appropriate to integrate any Follow functionality into Nano?

On the one hand, Follow also strives for simplicity and tries to mirror the request API. The other major advantage IMO is the inactivity timeout which is the only way to detect some kinds of error.

On the other hand, I am not sure if it is "feature bloat" or "mission creep" for Nano.

Do you have any thoughts about this?

URL injection

Nano sometimes interpolates user input without proper escaping.

Should we concern ourselves with protecting from URL injection? Or should the library user take care of that? Is this even a problem?

Included node_modules

Hi, Nuno. Is it correct to include node_modules in the Git code? I thought that is out of scope for source builds (obviously it is put there by npm install but that is a "binary build" IMO).

In my own code I always put node_modules in the .gitignore however I have no idea if that is correct or not.

Any thoughts?

[tests] make tests mocks

Instead of calling iris couch.

This would make travis-ci integration easier and tests would run much faster

CouchDB error objects vs. `Error` objects

I have struggled with this myself when using request directly. Often I want to do this:

do_it(function(error, body, header) {
    if(error)
        throw error;

    console.log("Everything is fine");
    // Do more stuff now.
})

Unfortunately, it is better to throw a new Error() instead of a plain object. This provides traceback information. So I must do this:

do_it(function(error, body, header) {
    if(error)
        throw new Error("Couch error: " + JSON.stringify(error));

    console.log("Everything is fine");
    // Do more stuff now.
})

But what if there was a socket error and I already have an Error object? It is all a bit messy.

What do you think about this (I have not tried it in code, just thinking about it now). When Couch returns an error:

  1. Create a new Error with perhaps a helpful description of what went wrong (e.g. "Failed to create document")
  2. Copy the CouchDB error object on top of the Javascript error object
  3. Return that in the callback

This way, the user has a choice:

do_it(tries, function on_done(error, body, header) {
    if(error.message == "no_db_file")
        return do_it(tries+1, on_done);
    else if(error)
        throw error;

    console.log("Everything is fine");
    // Do more stuff now.
})

Would that work or is there a problem with the idea? Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.