Offical Apache CouchDB library for Node.js.
Features:
...AsStream
functions only)nano
.npm install nano
or save nano
as a dependency of your project with
npm install --save nano
Note the minimum required version of Node.js is 6.
See Migration Guide for switching from Nano 6.x to 7.x.
To use nano
you need to connect it to your CouchDB install, to do that:
const nano = require('nano')('http://localhost:5984');
The URL you supply may also contain authentication credentials e.g. http://admin:mypassword@localhost:5984
.
To create a new database:
nano.db.create('alice');
and to use an existing database:
const alice = nano.db.use('alice');
Under-the-hood, calls like nano.db.create
are making HTTP API calls to the CouchDB service. Such operations are asynchronous. There are two ways to recieve the asynchronous data back from the library
nano.db.create('alice').then((data) => { // success - response is in 'data' }).catch((err) => { // failure - error information is in 'err' })
nano.db.create('alice', (err, data) => { // errors are in 'err' & response is in 'data' })
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.The documentation will now follow the Promises style.
A simple but complete example in the async/await style:
async function asyncCall() { await nano.db.destroy('alice') await nano.db.create('alice') const alice = nano.use('alice') const response = await alice.insert({ happy: true }, 'rabbit') return response } asyncCall()
or in the raw Promises-style
const nano = require('nano')('http://localhost:5984'); let alice; nano.db.destroy('alice').then((response) => { return nano.db.create('alice') }).then((response) => { alice = nano.use('alice') return alice.insert({ happy: true }, 'rabbit') }).then((response) => { console.log('you have inserted a document with an _id of rabbit') console.log(response); })
If you run either of these examples (after starting CouchDB) you will see:
you have inserted a document with an _id of rabbit. { ok: true, id: 'rabbit', rev: '1-6e4cb465d49c0368ac3946506d26335d' }
You can also see your document in futon.
Configuring nano to use your database server is as simple as:
const nano = require('nano')('http://localhost:5984') const db = nano.use('foo');
If you don't need to instrument database objects you can simply:
// nano parses the URL and knows this is a database const db = require('nano')('http://localhost:5984/foo');
You can also pass options to the require to specify further configuration options you can pass an object literal instead:
// nano parses the URL and knows this is a database const opts = { url: "http://localhost:5984/foo", requestDefaults: { "proxy" : "http://someproxy" }, log: (id, args) => { console.log(id, args); } }; const db = require('nano')(opts);
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 const couch = require('nano')( { url : "http://localhost:5984/prefix" parseUrl : false }); const db = couch.use('foo');
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:
const agentkeepalive = require('agentkeepalive'); const myagent = new agentkeepalive({ maxSockets: 50, maxKeepAliveRequests: 0, maxKeepAliveTime: 30000 }); const db = require('nano')( { url: "http://localhost:5984/foo", requestDefaults : { "agent" : myagent } });
There is a full TypeScript definition included in the the nano package. Your TypeScript editor will show you hints as you write your code with the nano library with your own custom classes:
import * as Nano from 'nano' let n = Nano('http://USERNAME:PASSWORD@localhost:5984') let db = n.db.use('people') interface iPerson extends Nano.MaybeDocument { name: string, dob: string } class Person implements iPerson { _id: string _rev: string name: string dob: string constructor(name: string, dob: string) { this._id = undefined this._rev = undefined this.name = name this.dob = dob } processAPIResponse(response: Nano.DocumentInsertResponse) { if (response.ok === true) { this._id = response.id this._rev = response.rev } } } let p = new Person('Bob', '2015-02-04') db.insert(p).then((response) => { p.processAPIResponse(response) console.log(p) })
Creates a CouchDB database with the given name
:
nano.db.create('alice').then((body) => { console.log('database alice created!'); })
Get information about the database name
:
nano.db.get('alice').then((body) => { console.log(body); })
Destroys the database name
:
nano.db.destroy('alice').then((body) => { // database destroyed })
Lists all the CouchDB databases:
nano.db.list().then((body) => { // body is an array body.forEach((db) => { console.log(db); }); });
Lists all the CouchDB databases as a stream:
nano.db.list().pipe(process.stdout);
Compacts name
, if designname
is specified also compacts its views.
Replicates source
to target
with options opts
. The target
database has to exist, add create_target:true
to opts
to create it prior to replication:
nano.db.replicate('alice', 'http://admin:password@otherhost.com:5984/alice', { create_target:true }).then((body) => { console.log(body); });
Enables replication using the new CouchDB api from source
to target
with options opts
. target
has to exist, add create_target:true
to opts
to create it prior to replication. Replication will survive server restarts.
nano.db.replication.enable('alice', 'http://admin:password@otherhost.com:5984/alice', { create_target:true }).then((body) => { console.log(body); });
Queries the state of replication using the new CouchDB API. The id
comes from the response given by the call to replication.enable
:
nano.db.replication.enable('alice', 'http://admin:password@otherhost.com:5984/alice', { create_target:true }).then((body) => { return nano.db.replication.query(body.id); }).then((response) => { console.log(response); });
Disables replication using the new CouchDB API. The id
comes from the response given by the call to replication.enable
:
nano.db.replication.enable('alice', 'http://admin:password@otherhost.com:5984/alice', { create_target:true }).then((body) => { return nano.db.replication.disable(body.id); }).then((response) => { console.log(response); });
Asks for the changes feed of name
, params
contains additions to the query string.
nano.db.changes('alice').then((body) => { console.log(body); });
Same as nano.db.changes
but returns a stream.
nano.db.changes('alice').pipe(process.stdout);
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:
const feed = db.follow({since: "now"}); feed.on('change', (change) => { console.log("change: ", change); }); feed.follow(); process.nextTick( () => { db.insert({"bar": "baz"}, "bar"); });
Gets database information:
nano.db.info().then((body) => { console.log('got database info', body); });
Returns a database object that allows you to perform operations against that database:
const alice = nano.use('alice'); alice.insert({ happy: true }, 'rabbit').then((body) => { // do something });
Alias for nano.use
Alias for nano.use
Alias for nano.use
Makes a custom request to CouchDB. This can be used to create your own HTTP request to the CouchDB server, to perform operations where there is no nano
function that encapsulates it. The available opts
are:
opts.db
– the database nameopts.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 nameopts.att
– the attachment nameopts.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 onesopts.body
– the document or attachment bodyopts.encoding
– the encoding for attachmentsopts.multipart
– array of objects for multipart requestopts.stream
- if true
, a request
object is returned. Default false
and a Promise is returned.Alias for nano.request
An object containing the nano
configurations, possible keys are:
url
- the CouchDB URLdb
- the database nameListen to db updates, the available params
are:
params.feed
– Type of feed. Can be one oflongpoll
: 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.** 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
const feed = nano.followUpdates({since: "now"}); feed.on('change', (change) => { console.log("change: ", change); }); feed.follow(); process.nextTick( () => { nano.db.create('alice'); });
Inserts doc
in the database with optional params
. If params is a string, it‘s assumed it is the intended document _id
. If params is an object, it’s passed as query string parameters and docName
is checked for defining the document _id
:
const alice = nano.use('alice'); alice.insert({ happy: true }, 'rabbit').then((body) => { 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.
const alice = nano.use('alice') alice.insert({ _id: 'myid', happy: true }).then((body) => { console.log(body) })
and also used to update an existing document, by including the _rev
token in the document being saved:
const alice = nano.use('alice') alice.insert({ _id: 'myid', _rev: '1-23202479633c2b380f79507a776743d5', happy: false }).then((body) => { console.log(body) })
Removes a document from CouchDB whose _id
is docname
and who's revision is _rev
:
alice.destroy('rabbit', '3-66c01cdf99e84c83a9b3fe65b88db8c0').then((body) => { console.log(body); });
Gets a document from CouchDB whose _id
is docname
:
alice.get('rabbit').then((body) => { console.log(body); });
or with optional query string params
:
alice.get('rabbit', { revs_info: true }).then((body) => { console.log(body); });
Same as get
but lightweight version that returns headers only:
alice.head('rabbit').then((headers) => { console.log(headers); });
Note: if you call alice.head
in the callback style, the headers are returned to you as the third argument of the callback function.
Copies the contents (and attachments) of a document to a new document, or overwrite an existing target document
alice.copy('rabbit', 'rabbit2', { overwrite: true }).then((body) => { console.log(body); });
Bulk operations(update/delete/insert) on the database, refer to the CouchDB doc e.g:
const documents = [ { a:1, b:2 }, { _id: 'tiger', striped: true} ]; alice.bulk({docs:documents}).then((body) => { console.log(body); });
List all the docs in the database .
alice.list().then((body) => { body.rows.forEach((doc) => { console.log(doc); }); });
or with optional query string additions params
:
alice.list({include_docs: true}).then((body) => { body.rows.forEach((doc) => { // output each document's body console.log(doc.doc); }); });
List all the docs in the database as a stream.
alice.list().pipe(process.stdout)
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
.
const keys = ['tiger', 'zebra', 'donkey']; alice.fetch({keys: keys}).then((data) => { console.log(data); });
** 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
.
Create index on database fields, as specified in CouchDB doc.
const indexDef = { index: { fields: ['foo'] }, name: 'fooindex' }; alice.createIndex(indexDef).then((result) => { console.log(result); });
Functions related to partitioned databses.
Create a partitioned database by passing { partitioned: true }
to db.create
:
await nano.db.create('my-partitioned-db', { partitioned: true })
The database can be used as normal:
const db = nano.db.use('my-partitioned-db')
but documents must have a two-part _id
made up of <partition key>:<document id>
. They are insert with db.insert
as normal:
const doc = { _id: 'canidae:dog', name: 'Dog', latin: 'Canis lupus familiaris' } await db.insert(doc)
Documents can be retrieved by their _id
using db.get
:
const doc = db.get('canidae:dog')
Mango indexes can be created to operate on a per-partition index by supplying partitioned: true
on creation:
const i = { ddoc: 'partitioned-query', index: { fields: ['name'] }, name: 'name-index', partitioned: true, type: 'json' } // instruct CouchDB to create the index await db.index(i)
Search indexes can be created by writing a design document with opts.partitioned = true
:
// the search definition const func = function(doc) { index('name', doc.name) index('latin', doc.latin) } // the design document containing the search definition function const ddoc = { _id: '_design/search-ddoc', indexes: { search-index: { index: func.toString() } }, options: { partitioned: true } } await db.insert(ddoc)
MapReduce views can be created by writing a design document with opts.partitioned = true
:
const func = function(doc) { emit(doc.family, doc.weight) } // Design Document const ddoc = { _id: '_design/view-ddoc', views: { family-weight: { map: func.toString(), reduce: '_sum' } }, options: { partitioned: true } } // create design document await db.insert(ddoc)
Fetch the stats of a single partition:
const stats = await alice.partitionInfo('canidae')
Fetch documents from a database partition:
// fetch document id/revs from a partition const docs = await alice.partitionedList('canidae') // add document bodies but limit size of response const docs = await alice.partitionedList('canidae', { include_docs: true, limit: 5 })
Fetch documents from a partition as a stream:
// fetch document id/revs from a partition nano.db.partitionedListAsStream('canidae').pipe(process.stdout) // add document bodies but limit size of response nano.db.partitionedListAsStream('canidae', { include_docs: true, limit: 5 }).pipe(process.stdout)
Query documents from a partition by supplying a Mango selector:
// find document whose name is 'wolf' in the 'canidae' partition await db.partitionedFind('canidae', { 'selector' : { 'name': 'Wolf' }})
Query documents from a partition by supplying a Mango selector as a stream:
// find document whose name is 'wolf' in the 'canidae' partition db.partitionedFindAsStream('canidae', { 'selector' : { 'name': 'Wolf' }}).pipe(process.stdout)
Search documents from a partition by supplying a Lucene query:
const params = { q: 'name:\'Wolf\'' } await db.partitionedSearch('canidae', 'search-ddoc', 'search-index', params) // { total_rows: ... , bookmark: ..., rows: [ ...] }
Search documents from a partition by supplying a Lucene query as a stream:
const params = { q: 'name:\'Wolf\'' } db.partitionedSearchAsStream('canidae', 'search-ddoc', 'search-index', params).pipe(process.stdout) // { total_rows: ... , bookmark: ..., rows: [ ...] }
Fetch documents from a MapReduce view from a partition:
const params = { startkey: 'a', endkey: 'b', limit: 1 } await db.partitionedView('canidae', 'view-ddoc', 'view-name', params) // { rows: [ { key: ... , value: [Object] } ] }
Fetch documents from a MapReduce view from a partition as a stream:
const params = { startkey: 'a', endkey: 'b', limit: 1 } db.partitionedView('canidae', 'view-ddoc', 'view-name', params).pipe(process.stdout) // { rows: [ { key: ... , value: [Object] } ] }
Inserts a doc
together with attachments
and params
. If params is a string, it's assumed as the intended document _id
. If params is an object, its passed as query string parameters and docName
is checked for defining the _id
. Refer to the doc for more details. The attachments
parameter must be an array of objects with name
, data
and content_type
properties.
const fs = require('fs'); fs.readFile('rabbit.png', (err, data) => { if (!err) { alice.multipart.insert({ foo: 'bar' }, [{name: 'rabbit.png', data: data, content_type: 'image/png'}], 'mydoc').then((body) => { console.log(body); }); } });
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').then((buffer) => { console.log(buffer.toString()); });
Inserts an attachment attname
to docname
, in most cases params.rev
is required. Refer to the CouchDB doc for more details.
const fs = require('fs'); fs.readFile('rabbit.png', (err, data) => { if (!err) { alice.attachment.insert('rabbit', 'rabbit.png', data, 'image/png', { rev: '12-150985a725ec88be471921a54ce91452' }).then((body) => { console.log(body); }); } });
It may be more memory-efficient to pipe a stream of data from a source (file, network etc) to a CouchDB attachment:
const rs = fs.createReadStream('logo.png'); const is = db.attachment.insertAsStream('mydoc', 'logo.png', null, 'image/png', { rev: '12-150985a725ec88be471921a54ce91452' }).on('end', () => { console.log('done') }); rs.pipe(is);
Get docname
's attachment attname
with optional query string additions params
.
const fs = require('fs'); alice.attachment.get('rabbit', 'rabbit.png').then((body) => { fs.writeFile('rabbit.png', body); });
const fs = require('fs'); alice.attachment.getAsStream('rabbit', 'rabbit.png').pipe(fs.createWriteStream('rabbit.png'));
changed in version 6
Destroy attachment attname
of docname
's revision rev
.
alice.attachment.destroy('rabbit', 'rabbit.png', {rev: '1-4701d73a08ce5c2f2983bf7c9ffd3320'}).then((body) => { console.log(body); });
Calls a view of the specified designname
with optional query string 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', 'happy_ones', { 'key': 'Tea Party', 'include_docs': true }).then((body) => { body.rows.forEach((doc) => { console.log(doc.value); }); });
or
alice.view('characters', 'soldiers', { 'keys': ['Hearts', 'Clubs'] }).then((body) => { body.rows.forEach((doc) => { console.log(doc.value); }); });
When params
is not supplied, or no keys are specified, it will simply return all documents in the view:
alice.view('characters', 'happy_ones').then((body) => { body.rows.forEach((doc) => { console.log(doc.value); }); });
alice.view('characters', 'happy_ones', { include_docs: true }).then((body) => { body.rows.forEach((doc) => { console.log(doc.value); }); });
Same as db.view
but returns a stream:
alice.view('characters', 'happy_ones', {reduce: false}).pipe(process.stdout);
Calls a list function fed by the given view from the specified design document.
alice.viewWithList('characters', 'happy_ones', 'my_list').then((body) => { console.log(body); });
Calls a show function from the specified design for the document specified by doc_id with optional query string additions params
.
alice.show('characters', 'format_doc', '3621898430').then((doc) => { console.log(doc); });
Take a look at the couchdb wiki for possible query paramaters and more information on show functions.
Calls the design's update function with the specified doc in input.
db.atomic("update", "inplace", "foobar", {field: "foo", value: "bar"}).then((response) => { console.log(response); });
Note that the data is sent in the body of the request. An example update handler follows:
"updates": { "in-place" : "function(doc, req) { var request_body = JSON.parse(req.body); var field = request_body.field; var value = request_body.value; var message = 'set ' + field + ' to ' + value; doc[field] = value; return [doc, message]; }" }
Calls a view of the specified design with optional query string additions params
.
alice.search('characters', 'happy_ones', { q: 'cat' }).then((doc) => { console.log(doc); });
or
const drilldown = [['author', 'Dickens']['publisher','Penguin']] alice.search('inventory', 'books', { q: '*:*', drilldown: drilldown }).then((doc) => { console.log(doc); });
Check out the tests for a fully functioning example.
Calls a view of the specified design with optional query string additions params
. Returns stream.
alice.search('characters', 'happy_ones', { q: 'cat' }).pipe(process.stdout);
Perform a “Mango” query by supplying a JavaScript object containing a selector:
// find documents where the name = "Brian" and age > 25. const q = { selector: { name: { "$eq": "Brian"}, age : { "$gt": 25 } }, fields: [ "name", "age", "tags", "url" ], limit:50 }; alice.find(q).then((doc) => { console.log(doc); });
Perform a “Mango” query by supplying a JavaScript object containing a selector, but return a stream:
// find documents where the name = "Brian" and age > 25. const q = { selector: { name: { "$eq": "Brian"}, age : { "$gt": 25 } }, fields: [ "name", "age", "tags", "url" ], limit:50 }; alice.findAsStream(q).pipe(process.stdout);
Nano supports making requests using CouchDB's cookie authentication functionality. If you initialise Nano so that it is cookie-aware, you may call nano.auth
first to get a session cookie. Nano will behave like a web browser, remembering your session cookie and refreshing it if a new one is received in a future HTTP response.
const nano = require('nano')({url: 'http://localhost:5984', requestDefaults: {jar:true}}), username = 'user', userpass = 'pass', db = nano.db.use('mydb'); nano.auth(username, userpass).then((() => { return db.get('mydoc'); }).then((doc) => { console.log(doc); });
The second request works because the nano
library has remembered the AuthSession
cookie that was invisibily returned by the nano.auth
call.
When you have a session, you can see what permissions you have by calling the nano.session
function
nano.session().then((doc) => { console.log(doc) // { userCtx: { roles: [ '_admin', '_reader', '_writer' ], name: 'rita' }, ok: true } });
If your application needs to generate UUIDs, then CouchDB can provide some for you
nano.uuids(3).then((doc) => { console.log(doc); }); // { uuids: [ // '5d1b3ef2bc7eea51f660c091e3dffa23', // '5d1b3ef2bc7eea51f660c091e3e006ff', // '5d1b3ef2bc7eea51f660c091e3e007f0', //]}
The first parameter is the number of uuids to generate. If omitted, it defaults to 1.
nano
is minimalistic but you can add your own features with nano.request(opts)
For example, to create a function to retrieve a specific revision of the rabbit
document:
function getrabbitrev(rev) { return nano.request({ db: 'alice', doc: 'rabbit', method: 'get', params: { rev: rev } }); } getrabbitrev('4-2e6cdc4c7e26b745c2881a24e0eeece2').then((body) => { console.log(body); });
You can pipe the return values of certain nano functions like other stream. For example if our rabbit
document has an attachment with name picture.png
you can pipe it to a writable stream
:
const fs = require('fs'); const nano = require('nano')('http://127.0.0.1:5984/'); const alice = nano.use('alice'); alice.attachment.getAsStream('rabbit', 'picture.png').pipe(fs.createWriteStream('/tmp/rabbit.png'));
then open /tmp/rabbit.png
and you will see the rabbit picture.
Functions that return streams instead of a Promise are:
attachment functions:
and document level functions
Check issues
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.
git clone git://github.com/apache/couchdb-nano.git
To create a new release of nano. Run the following commands on the master branch
npm version {patch|minor|major} github push origin master --tags npm publish