- Notifications
You must be signed in to change notification settings - Fork164
Nano: The official Apache CouchDB library for Node.js
License
apache/couchdb-nano
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
OfficalApache CouchDB library forNode.js.
Features:
- Minimalistic - There is only a minimum of abstraction between you andCouchDB.
- Pipes - Proxy requests from CouchDB directly to your end user. (
...AsStream
functions only) - Promises - The vast majority of library calls return native Promises.
- TypeScript - Detailed TypeScript definitions are built in.
- Errors - Errors are proxied directly from CouchDB: if you know CouchDByou already know
nano
.
- Installnpm
npm install nano
or savenano
as a dependency of your project with
npm install --save nano
Note the minimum required version of Node.js is 10.
- nano.db.create(name, [callback])
- nano.db.get(name, [callback])
- nano.db.destroy(name, [callback])
- nano.db.list([callback])
- nano.db.listAsStream()
- nano.db.compact(name, [designname], [callback])
- nano.db.replicate(source, target, [opts], [callback])
- nano.db.replication.enable(source, target, [opts], [callback])
- nano.db.replication.query(id, [opts], [callback])
- nano.db.replication.disable(id, [opts], [callback])
- nano.db.changes(name, [params], [callback])
- nano.db.changesAsStream(name, [params])
- nano.db.info([callback])
- nano.use(name)
- nano.request(opts, [callback])
- nano.config
- nano.updates([params], [callback])
- nano.info([callback])
- db.insert(doc, [params], [callback])
- db.destroy(docname, rev, [callback])
- db.get(docname, [params], [callback])
- db.head(docname, [callback])
- db.bulk(docs, [params], [callback])
- db.list([params], [callback])
- db.listAsStream([params])
- db.fetch(docnames, [params], [callback])
- db.fetchRevs(docnames, [params], [callback])
- db.createIndex(indexDef, [callback])
- db.changesReader
Partitioned database functions
- db.partitionInfo(partitionKey, [callback]))
- db.partitionedList(partitionKey, [params], [callback])
- db.partitionedListAsStream(partitionKey, [params])
- db.partitionedFind(partitionKey, query, [callback])
- db.partitionedFindAsStream(partitionKey, query)
- db.partitionedSearch(partitionKey, designName, searchName, params, [callback])
- db.partitionedSearchAsStream(partitionKey, designName, searchName, params)
- db.partitionedView(partitionKey, designName, viewName, [params], [callback])
- db.partitionedViewAsStream(partitionKey, designName, viewName, [params])
- db.attachment.insert(docname, attname, att, contenttype, [params], [callback])
- db.attachment.insertAsStream(docname, attname, att, contenttype, [params])
- db.attachment.get(docname, attname, [params], [callback])
- db.attachment.getAsStream(docname, attname, [params])
- db.attachment.destroy(docname, attname, [params], [callback])
- db.view(designname, viewname, [params], [callback])
- db.viewAsStream(designname, viewname, [params])
- db.viewWithList(designname, viewname, listname, [params])
- db.viewWithListAsStream(designname__viewname, listname, [params])
- db.show(designname, showname, doc_id, [params], [callback])
- db.atomic(designname, updatename, docname, [body], [callback])
- db.search(designname, viewname, params, [callback])
- db.searchAsStream(designname, viewname, params)
- db.find(selector, [callback])
- db.findAsStream(selector)
To usenano
you need to connect it to your CouchDB install, to do that:
constnano=require('nano')('http://localhost:5984');
Note: Supplying authentication credentials in the URL e.g.
http://admin:mypassword@localhost:5984
is deprecated. Usenano.auth
instead.
To create a new database:
nano.db.create('alice');
and to use an existing database:
constalice=nano.db.use('alice');
Under-the-hood, calls likenano.db.create
are making HTTP API calls to the CouchDB service. Such operations areasynchronous. There are two ways to receive the asynchronous data back from the library
- Promises
nano.db.create('alice').then((data)=>{// success - response is in 'data'}).catch((err)=>{// failure - error information is in 'err'})
or in the async/await style:
try{constresponse=awaitnano.db.create('alice')// succeededconsole.log(response)}catch(e){// failedconsole.error(e)}
- Callbacks
nano.db.create('alice',(err,data)=>{// errors are in 'err' & response is in 'data'})
Innano
the callback function receives always three arguments:
err
- The error, if any.body
- The HTTPresponse body from CouchDB, if no error.JSON parsed body, binary for non JSON responses.header
- The HTTPresponse header from CouchDB, if no error.
The documentation will follow theasync/await style.
A simple but complete example in theasync/await style:
asyncfunctionasyncCall(){awaitnano.db.destroy('alice')awaitnano.db.create('alice')constalice=nano.use('alice')constresponse=awaitalice.insert({happy:true},'rabbit')returnresponse}asyncCall()
Running this example will produce:
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 (http://localhost:5984/_utils).
Configuring nano to use your database server is as simple as:
constnano=require('nano')('http://localhost:5984')constdb=nano.use('foo');
If you don't need to instrument database objects you can simply:
// nano parses the URL and knows this is a databaseconstdb=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 databaseconstopts={url:'http://localhost:5984/foo',requestDefaults:{proxy:{protocol:'http',host:'myproxy.net'},headers:{customheader:'MyCustomHeader'}}};constdb=require('nano')(opts);
Nano works perfectly well over HTTPS as long as the SSL cert is signed by a certification authority known by your client operating system. If you have a custom or self-signed certificate, you may need to create your own HTTPS agent and pass it to Nano e.g.
consthttpsAgent=newhttps.Agent({ca:'/path/to/cert',rejectUnauthorized:true,keepAlive:true,maxSockets:6})constnano=Nano({url:process.env.COUCH_URL,requestDefaults:{agent:httpsAgent,}})
Please checkaxios for more information on the defaults. They support features like proxies, timeout 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 rootconstcouch=require('nano')({url :"http://localhost:5984/prefix"parseUrl :false});constdb=couch.use('foo');
A very important configuration parameter if you have a high traffic website and are usingnano
is the HTTP pool size. By default, the Node.js HTTP global agent has a infinite number of active connections that can run simultaneously. This can be limited to user-defined number (maxSockets
) of requests that are "in flight", while others are kept in a queue. Here's an example explicitly using the Node.js HTTP agent configured withcustom options:
consthttp=require('http')constmyagent=newhttp.Agent({keepAlive:true,maxSockets:25})constdb=require('nano')({url:'http://localhost:5984/foo',requestDefaults :{agent :myagent}});
There is a full TypeScript definition included in the thenano package. Your TypeScript editor will show you hints as you write your code with thenano library with your own custom classes:
import*asNanofrom'nano'letn=Nano('http://USERNAME:PASSWORD@localhost:5984')letdb=n.db.use('people')interfaceiPersonextendsNano.MaybeDocument{name:string,dob:string}classPersonimplementsiPerson{_id:string_rev:stringname:stringdob:stringconstructor(name:string,dob:string){this._id=undefinedthis._rev=undefinedthis.name=namethis.dob=dob}processAPIResponse(response:Nano.DocumentInsertResponse){if(response.ok===true){this._id=response.idthis._rev=response.rev}}}letp=newPerson('Bob','2015-02-04')db.insert(p).then((response)=>{p.processAPIResponse(response)console.log(p)})
Creates a CouchDB database with the givenname
, with optionsopts
.
awaitnano.db.create('alice',{n:3})
Get information about the databasename
:
constinfo=awaitnano.db.get('alice')
Destroys the databasename
:
awaitnano.db.destroy('alice')
Lists all the CouchDB databases:
constdblist=awaitnano.db.list()
Lists all the CouchDB databases as a stream:
nano.db.listAsStream().on('error',(e)=>console.error('error',e)).pipe(process.stdout);
Compactsname
, ifdesignname
is specified also compacts its views.
Replicatessource
totarget
with optionsopts
. Thetarget
databasehas to exist, addcreate_target:true
toopts
to create it prior toreplication:
constresponse=awaitnano.db.replicate('alice','http://admin:password@otherhost.com:5984/alice',{create_target:true})
Enables replication using the new CouchDB api fromsource
totarget
with optionsopts
.target
has to exist, addcreate_target:true
toopts
to create it prior to replication. Replication will survive server restarts.
constresponse=awaitnano.db.replication.enable('alice','http://admin:password@otherhost.com:5984/alice',{create_target:true})
Queries the state of replication using the new CouchDB API. Theid
comes from the responsegiven by the call toreplication.enable
:
constr=awaitnano.db.replication.enable('alice','http://admin:password@otherhost.com:5984/alice',{create_target:true})constq=awaitnano.db.replication.query(r.id)
Disables replication using the new CouchDB API. Theid
comes from the response givenby the call toreplication.enable
:
constr=awaitnano.db.replication.enable('alice','http://admin:password@otherhost.com:5984/alice',{create_target:true})awaitnano.db.replication.disable(r.id);
Asks for the changes feed ofname
,params
contains additionsto the query string.
constc=awaitnano.db.changes('alice')
Same asnano.db.changes
but returns a stream.
nano.db.changes('alice').pipe(process.stdout);
Gets database information:
constinfo=awaitnano.db.info()
Returns a database object that allows you to perform operations against that database:
constalice=nano.use('alice');awaitalice.insert({happy:true},'rabbit')
The database object can be used to access theDocument Functions.
Alias fornano.use
Alias fornano.use
Alias fornano.use
Makes a custom request to CouchDB. This can be used to create your own HTTP request to the CouchDBserver, to perform operations where there is nonano
function that encapsulates it. The availableopts
are:
opts.db
– the database nameopts.method
– the http method, defaults toget
opts.path
– the full path of the request, overridesopts.doc
andopts.att
opts.doc
– the document nameopts.att
– the attachment nameopts.qs
– query string parameters, appended after any existingopts.path
,opts.doc
, oropts.att
opts.content_type
– the content type of the request, default tojson
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
- iftrue
, arequest
object is returned. Defaultfalse
and a Promise is returned.
Alias fornano.request
An object containing thenano
configurations, possible keys are:
url
- the CouchDB URLdb
- the database name
Listen to db updates, the availableparams
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.
Fetch information about the CouchDB cluster:
constinfo=awaitnano.info()
The response is an object withCouchDB cluster information.
Insertsdoc
in the database with optionalparams
. 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 anddocName
is checked for defining the document_id
:
constalice=nano.use('alice');constresponse=awaitalice.insert({happy:true},'rabbit')
Theinsert
function can also be used with the method signaturedb.insert(doc,[callback])
, where thedoc
contains the_id
field e.g.
constalice=nano.use('alice')constresponse=awaitalice.insert({_id:'myid',happy:true})
and also used to update an existing document, by including the_rev
token in the document being saved:
constalice=nano.use('alice')constresponse=awaitalice.insert({_id:'myid',_rev:'1-23202479633c2b380f79507a776743d5',happy:false})
Removes a document from CouchDB whose_id
isdocname
and whose revision (_rev
) isrev
:
constresponse=awaitalice.destroy('rabbit','3-66c01cdf99e84c83a9b3fe65b88db8c0')
Gets a document from CouchDB whose_id
isdocname
:
constdoc=awaitalice.get('rabbit')
or with optionalquery stringparams
:
constdoc=awaitalice.get('rabbit',{revs_info:true})
If you passattachments=true
, thedoc._attachments.attachmentNameN.data
fields will contain thebase-64 encoded attachments.Or, you can usedb.multipart.get
and parse the returned buffer to get the document and attachments.
See theattachments methods to retrievejust an attachment.
Same asget
but lightweight version that returns headers only:
constheaders=awaitalice.head('rabbit')
Note: if you callalice.head
in the callback style, the headers are returned to you as the third argument of the callback function.
Bulk operations(update/delete/insert) on the database, refer to theCouchDB doc e.g:
constdocuments=[{a:1,b:2},{_id:'tiger',striped:true}];constresponse=awaitalice.bulk({docs:documents})
List all the docs in the database .
constdoclist=awaitalice.list().then((body)=>{body.rows.forEach((doc)=>{console.log(doc);})});
or with optional query string additionsparams
:
constdoclist=awaitalice.list({include_docs:true})
List all the docs in the database as a stream.
alice.listAsStream().on('error',(e)=>console.error('error',e)).pipe(process.stdout)
Bulk fetch of the database documents,docnames
are specified as perCouchDB doc.additional query stringparams
can be specified,include_docs
is always settotrue
.
constkeys=['tiger','zebra','donkey'];constdatat=awaitalice.fetch({keys:keys})
** changed in version 6 **
Bulk fetch of the revisions of the database documents,docnames
are specified as perCouchDB doc.additional query stringparams
can be specified, this is the same method as fetch butinclude_docs
is not automatically set totrue
.
Create index on database fields, as specified inCouchDB doc.
constindexDef={index:{fields:['foo']},name:'fooindex'};constresponse=awaitalice.createIndex(indexDef)
Nano provides a low-level API for making calls to CouchDB's changes feed, or if you want areliable, resumable changes feed follower, then you need thechangesReader
.
There are three ways to start listening to the changes feed:
changesReader.start()
- to listen to changes indefinitely by repeated "long poll" requests. This mode continues to poll for changes untilchangesReader.stop()
is called, at which point any active long poll will be canceled.changesReader.get()
- to listen to changes until the end of the changes feed is reached, by repeated "long poll" requests. Once a response with zero changes is received, the 'end' event will indicate the end of the changes and polling will stop.changesReader.spool()
- listen to changes in one long HTTP request. (as opposed to repeated round trips) - spool is faster but less reliable.
Note: for
.get()
&.start()
, the sequence of API calls can be paused by callingchangesReader.pause()
and resumed by callingchangesReader.resume()
.
Set up your database connection and then choosechangesReader.start()
to listen to that database's changes:
constdb=nano.db.use('mydb')db.changesReader.start().on('change',(change)=>{console.log(change)}).on('batch',(b)=>{console.log('a batch of',b.length,'changes has arrived');}).on('seq',(s)=>{console.log('sequence token',s);}).on('error',(e)=>{console.error('error',e);})
Note: you probably want to monitoreither the
change
orbatch
event, not both.
If you wantchangesReader
to hold off making the next_changes
API call until you are ready, then supplywait:true
in the options toget
/start
. The next request will only fire when you callchangesReader.resume()
:
db.changesReader.get({wait:true}).on('batch',(b)=>{console.log('a batch of',b.length,'changes has arrived');// do some asynchronous work here and call "changesReader.resume()"// when you're ready for the next API call to be dispatched.// In this case, wait 5s before the next changes feed request.setTimeout(()=>{db.changesReader.resume()},5000)}).on('end',()=>{console.log('changes feed monitoring has stopped');});
You may supply a number of options when you start to listen to the changes feed:
Parameter | Description | Default value | e.g. | |
---|---|---|---|---|
batchSize | The maximum number of changes to ask CouchDB for per HTTP request. This is the maximum number of changes you will receive in abatch event. | 100 | 500 | |
since | The position in the changes feed to start from where0 means the beginning of time,now means the current position or a string token indicates a fixed position in the changes feed | now | 390768-g1AAAAGveJzLYWBgYMlgTmGQ | |
includeDocs | Whether to include document bodies or not | false | e.g. true | |
wait | Forget /start mode, automatically pause the changes reader after each request. When the the user callsresume() , the changes reader will resume. | false | e.g. true | |
fastChanges | Adds a seq_interval parameter to fetch changes more quickly | false | true | |
selector | Filters the changes feed with the supplied Mango selector | null | {"name":"fred} | |
timeout | The number of milliseconds a changes feed request waits for data | 60000 | 10000 |
The events it emits are as follows:s
Event | Description | Data | |
---|---|---|---|
change | Each detected change is emitted individually. Only available inget /start modes. | A change object | |
batch | Each batch of changes is emitted in bulk in quantities up tobatchSize . | An array of change objects | |
seq | Each new sequence token (per HTTP request). This token can be passed intoChangesReader as thesince parameter to resume changes feed consumption from a known point. Only available inget /start modes. | String | |
error | On a fatal error, a descriptive object is returned and change consumption stops. | Error object | |
end | Emitted when the end of the changes feed is reached.ChangesReader.get() mode only, | Nothing |
TheChangesReader library will handle many temporal errors such as network connectivity, service capacity limits and malformed data but it will emit anerror
event and exit when fed incorrect authentication credentials or an invalidsince
token.
Thechange
event delivers a change object that looks like this:
{"seq":"8-g1AAAAYIeJyt1M9NwzAUBnALKiFOdAO4gpRix3X","id":"2451be085772a9e588c26fb668e1cc52","changes":[{"rev":"4-061b768b6c0b6efe1bad425067986587"}],"doc":{"_id":"2451be085772a9e588c26fb668e1cc52","_rev":"4-061b768b6c0b6efe1bad425067986587","a":3}}
N.B
doc
is only present ifincludeDocs:true
is suppliedseq
is not present for every change
Theid
is the unique identifier of the document that changed and thechanges
array contains the document revision tokens that were written to the database.
Thebatch
event delivers an array of change objects.
Functions related topartitioned databases.
Create a partitioned database by passing{ partitioned: true }
todb.create
:
awaitnano.db.create('my-partitioned-db',{partitioned:true})
The database can be used as normal:
constdb=nano.db.use('my-partitioned-db')
but documents must have a two-part_id
made up of<partition key>:<document id>
. They are insert withdb.insert
as normal:
constdoc={_id:'canidae:dog',name:'Dog',latin:'Canis lupus familiaris'}awaitdb.insert(doc)
Documents can be retrieved by their_id
usingdb.get
:
constdoc=db.get('canidae:dog')
Mango indexes can be created to operate on a per-partition index by supplyingpartitioned: true
on creation:
consti={ddoc:'partitioned-query',index:{fields:['name']},name:'name-index',partitioned:true,type:'json'}// instruct CouchDB to create the indexawaitdb.index(i)
Search indexes can be created by writing a design document withopts.partitioned = true
:
// the search definitionconstfunc=function(doc){index('name',doc.name)index('latin',doc.latin)}// the design document containing the search definition functionconstddoc={_id:'_design/search-ddoc',indexes:{search-index:{index:func.toString()}},options:{partitioned:true}}awaitdb.insert(ddoc)
MapReduce views can be created by writing a design document withopts.partitioned = true
:
constfunc=function(doc){emit(doc.family,doc.weight)}// Design Documentconstddoc={_id:'_design/view-ddoc',views:{family-weight:{map:func.toString(),reduce:'_sum'}},options:{partitioned:true}}// create design documentawaitdb.insert(ddoc)
Fetch the stats of a single partition:
conststats=awaitalice.partitionInfo('canidae')
Fetch documents from a database partition:
// fetch document id/revs from a partitionconstdocs=awaitalice.partitionedList('canidae')// add document bodies but limit size of responseconstdocs=awaitalice.partitionedList('canidae',{include_docs:true,limit:5})
Fetch documents from a partition as a stream:
// fetch document id/revs from a partitionnano.db.partitionedListAsStream('canidae').on('error',(e)=>console.error('error',e)).pipe(process.stdout)// add document bodies but limit size of responsenano.db.partitionedListAsStream('canidae',{include_docs:true,limit:5}).on('error',(e)=>console.error('error',e)).pipe(process.stdout)
Query documents from a partition by supplying a Mango selector:
// find document whose name is 'wolf' in the 'canidae' partitionawaitdb.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' partitiondb.partitionedFindAsStream('canidae',{'selector' :{'name':'Wolf'}}).on('error',(e)=>console.error('error',e)).pipe(process.stdout)
Search documents from a partition by supplying a Lucene query:
constparams={q:'name:\'Wolf\''}awaitdb.partitionedSearch('canidae','search-ddoc','search-index',params)// { total_rows: ... , bookmark: ..., rows: [ ...] }
Search documents from a partition by supplying a Lucene query as a stream:
constparams={q:'name:\'Wolf\''}db.partitionedSearchAsStream('canidae','search-ddoc','search-index',params).on('error',(e)=>console.error('error',e)).pipe(process.stdout)// { total_rows: ... , bookmark: ..., rows: [ ...] }
Fetch documents from a MapReduce view from a partition:
constparams={startkey:'a',endkey:'b',limit:1}awaitdb.partitionedView('canidae','view-ddoc','view-name',params)// { rows: [ { key: ... , value: [Object] } ]}
Fetch documents from a MapReduce view from a partition as a stream:
constparams={startkey:'a',endkey:'b',limit:1}db.partitionedViewAsStream('canidae','view-ddoc','view-name',params).on('error',(e)=>console.error('error',e)).pipe(process.stdout)// { rows: [ { key: ... , value: [Object] } ]}
Inserts adoc
together withattachments
andparams
. If params is a string, it's assumed as the intended document_id
. If params is an object, its passed as query string parameters anddocName
is checked for defining the_id
. Refer to thedoc for more details.Theattachments
parameter must be an array of objects withname
,data
andcontent_type
properties.
constfs=require('fs');fs.readFile('rabbit.png',(err,data)=>{if(!err){awaitalice.multipart.insert({foo:'bar'},[{name:'rabbit.png',data:data,content_type:'image/png'}],'mydoc')}});
Getdocname
together with its attachments viamultipart/related
request with optionalquery string additions. The multipart response body is aBuffer
.
constresponse=awaitalice.multipart.get('rabbit')
Inserts an attachmentattname
todocname
, in most casesparams.rev
is required. Refer to theCouchDB doc for more details.
constfs=require('fs');fs.readFile('rabbit.png',(err,data)=>{if(!err){awaitalice.attachment.insert('rabbit','rabbit.png',data,'image/png',{rev:'12-150985a725ec88be471921a54ce91452'})}});
As of Nano 9.x, the functiondb.attachment.insertAsStream
is now deprecated. Now simply passa readable stream todb.attachment.insert
as the third paramseter.
Getdocname
's attachmentattname
with optional query string additionsparams
.
constfs=require('fs');constbody=awaitalice.attachment.get('rabbit','rabbit.png')fs.writeFile('rabbit.png',body)
constfs=require('fs');alice.attachment.getAsStream('rabbit','rabbit.png').on('error',e=>console.error).pipe(fs.createWriteStream('rabbit.png'));
changed in version 6
Destroy attachmentattname
ofdocname
's revisionrev
.
constresponse=awaitalice.attachment.destroy('rabbit','rabbit.png',{rev:'1-4701d73a08ce5c2f2983bf7c9ffd3320'})
Calls a view of the specifieddesignname
with optional query stringparams
. If you're looking to filter the view results by key(s) pass an array of keys, e.g{ keys: ['key1', 'key2', 'key_n'] }
, asparams
.
constbody=awaitalice.view('characters','happy_ones',{key:'Tea Party',include_docs:true})body.rows.forEach((doc)=>{console.log(doc.value)})
or
constbody=awaitalice.view('characters','soldiers',{keys:['Hearts','Clubs']})
Whenparams
is not supplied, or no keys are specified, it will simply return all documents in the view:
constbody=awaitalice.view('characters','happy_ones')
constbody=alice.view('characters','happy_ones',{include_docs:true})
Same asdb.view
but returns a stream:
alice.viewAsStream('characters','happy_ones',{reduce:false}).on('error',(e)=>console.error('error',e)).pipe(process.stdout);
Calls a list function fed by the given view from the specified design document.
constbody=awaitalice.viewWithList('characters','happy_ones','my_list')
Calls a list function fed by the given view from the specified design document as a stream.
alice.viewWithListAsStream('characters','happy_ones','my_list').on('error',(e)=>console.error('error',e)).pipe(process.stdout);
Calls a show function from the specified design for the document specified by doc_id withoptional query string additionsparams
.
constdoc=awaitalice.show('characters','format_doc','3621898430')
Take a look at theCouchDB wikifor possible query paramaters and more information on show functions.
Calls the design's update function with the specified doc in input.
constresponse=awaitdb.atomic('update','inplace','foobar',{field:'foo',value:'bar'})
Note that the data is sent in the body of the request.An example update handler follows:
"updates":{"in-place" : "function(doc, req) {varrequest_body=JSON.parse(req.body)varfield=request_body.fieldvarvalue=request_body.valuevarmessage='set '+field+' to '+valuedoc[field]=valuereturn[doc,message]}"}
Calls a view of the specified design with optional query string additionsparams
.
constresponse=awaitalice.search('characters','happy_ones',{q:'cat'})
or
constdrilldown=[['author','Dickens']['publisher','Penguin']]constresponse=awaitalice.search('inventory','books',{q:'*:*',drilldown:drilldown})
Check out the tests for a fully functioning example.
Calls a view of the specified design with optional query string additionsparams
. 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.constq={selector:{name:{"$eq":"Brian"},age :{"$gt":25}},fields:["name","age","tags","url"],limit:50};constresponse=awaitalice.find(q)
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.constq={selector:{name:{"$eq":"Brian"},age :{"$gt":25}},fields:["name","age","tags","url"],limit:50};alice.findAsStream(q).on('error',(e)=>console.error('error',e)).pipe(process.stdout);
Nano supports making requests using CouchDB'scookie authentication functionality. If you initialiseNano so that it is cookie-aware, you may callnano.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.
constnano=require('nano')({url:'http://localhost:5984',requestDefaults:{jar:true}})constusername='user'constuserpass='pass'constdb=nano.db.use('mydb')// authenticateawaitnano.auth(username,userpass)// requests from now on are authenticatedconstdoc=awaitdb.get('mydoc')console.log(doc)
The second request works because thenano
library has remembered theAuthSession
cookie that was invisibily returned by thenano.auth
call.
When you have a session, you can see what permissions you have by calling thenano.session
function
constdoc=awaitnano.session()// { userCtx: { roles: [ '_admin', '_reader', '_writer' ], name: 'rita' }, ok: true}
If your application needs to generate UUIDs, then CouchDB can provide some for you
constresponse=awaitnano.uuids(3)// { 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 withnano.request(opts)
For example, to create a function to retrieve a specific revision of therabbit
document:
functiongetrabbitrev(rev){returnnano.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 ourrabbit
document has an attachment with namepicture.png
you can pipe it to awritable stream
:
constfs=require('fs');constnano=require('nano')('http://127.0.0.1:5984/');constalice=nano.use('alice');alice.attachment.getAsStream('rabbit','picture.png').on('error',(e)=>console.error('error',e)).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:
- nano.db.listAsStream
attachment functions:
- db.attachment.getAsStream
- db.attachment.insertAsStream
and document level functions
- db.listAsStream
When instantiating Nano, you may supply the function that will perform the logging of requests and responses. In its simplest for, simply passconsole.log
as your logger:
constnano=Nano({url:process.env.COUCH_URL,log:console.log})// all requests and responses will be sent to console.log
You may supply your own logging function to format the data before output:
consturl=require('url')constlogger=(data)=>{// only output logging if there is an environment variable setif(process.env.LOG==='nano'){// if this is a requestif(typeofdata.err==='undefined'){constu=newurl.URL(data.uri)console.log(data.method,u.pathname,data.qs)}else{// this is a responseconstprefix=data.err ?'ERR' :'OK'console.log(prefix,data.headers.statusCode,JSON.stringify(data.body).length)}}}constnano=Nano({url:process.env.COUCH_URL,log:logger})// all requests and responses will be formatted by my code// GET /cities/_all_docs { limit: 5 }// OK 200 468
- article:nano - a minimalistic CouchDB client for nodejs
- article:getting started with Node.js and CouchDB
- article:nano 3
- article:how to update a document with nano
- article:thoughts on development using CouchDB with Node.js
- example in the wild:nanoblog
Checkissues
To run (and configure) the test suite simply:
cd nanonpm installnpm runtest
- code:
git clone git://github.com/apache/couchdb-nano.git
- home:https://github.com/apache/couchdb-nano
- bugs:https://github.com/apache/couchdb-nano/issues
- chat:Freenode IRC @ #couchdb-dev
To create a new release of nano. Run the following commands on the main branch
npm version {patch|minor|major} github push origin main --tags npm publish
About
Nano: The official Apache CouchDB library for Node.js