nano

repository·main·Indexed 20 days ago

https://github.com/apache/couchdb-nano

The official Apache CouchDB client library for Node.js. It provides a minimalistic, Promise-based interface that mirrors the CouchDB HTTP API, supporting database administration, document management, bulk operations, attachments, and partitioned databases. It includes a specialized changesReader for reliable, resumable changes feed monitoring.

Tokens
10.6K
Snippets
43
Records
48
Agent score
72%

What's inside nano

  1. Access a specific database with nano.use()

    main

    To perform operations on a specific database (like inserting documents or fetching data), use nano.use(name). This returns a database object that provides access to Document Functions.

    Note: nano.db.use(name), nano.db.scope(name), and nano.scope(name) are all aliases for nano.use(name).

    const alice = nano.use('alice');
    await alice.insert({ happy: true }, 'rabbit')
  2. How to follow the CouchDB changes feed with changesReader

    main

    For reliable, resumable changes feed following, use the changesReader abstraction. It provides three distinct modes of operation:

    1. changesReader.start(): Listens indefinitely using repeated "long poll" requests. Continues until changesReader.stop() is called.
    2. changesReader.get(): Listens until the end of the changes feed is reached. Polling stops once a response with zero changes is received (triggering the end event).
    3. changesReader.spool(): Listens to changes in a single long HTTP request. This is faster but less reliable than the polling modes.

    For .get() and .start() modes, you can control the flow using changesReader.pause() and changesReader.resume().

    const db = 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);
      })
  3. Use cookie authentication with nano.auth()

    main

    Nano supports CouchDB's cookie authentication. If you initialize Nano to be cookie-aware, you can call nano.auth(username, userpass) to establish a session. Nano will automatically manage and refresh the AuthSession cookie for subsequent requests, behaving like a web browser.

    You can check your current permissions/session by calling nano.session().

    const nano = require('nano')({
      url: 'http://127.0.0.1:5984'
    })
    const username = 'user'
    const userpass = 'pass'
    const db = nano.db.use('mydb')
    
    // Authenticate and establish session cookie
    await nano.auth(username, userpass)
    
    // Subsequent requests are automatically authenticated
    const doc = await db.get('mydoc')
    
    // Check session info
    const session = await nano.session()
  4. Install Nano via npm

    main

    Install the nano library using npm. Note that the minimum required Node.js version is 10.

    Important Compatibility Note: Nano version 11.0.0 and later is a breaking change for Node.js versions 16 and older. Nano 11 uses the built-in fetch HTTP client, which requires Node.js 18 or later. If you are using Node 16 or older, you must continue using Nano 10.

    npm install nano
    # or
    npm install --save nano
  5. Use Promises or async/await instead of callbacks in Nano 11

    main

    Nano 11 has removed support for callbacks. All asynchronous operations now return Promises. If your existing codebase relies on passing a callback as the last argument to Nano functions, you must refactor your code to use .then() or the await pattern to avoid breakage.

    // Using .then()
    db.list().then((data) => { console.log('response', data )})
    
    // Using await
    const data = await db.list()
    console.log('response', data)
  6. Work with partitioned databases

    main

    Partitioned databases require specific handling for IDs and indexing. Documents must use a two-part _id in the format <partition key>:<document id>.

    Creating a partitioned database

    await nano.db.create('my-partitioned-db', { partitioned: true })

    Inserting and retrieving documents

    Documents are inserted normally, but the _id must include the partition key:

    const doc = { _id: 'canidae:dog', name: 'Dog' }
    await db.insert(doc)
    
    // Retrieval
    const doc = await db.get('canidae:dog')

    Creating Partitioned Indexes

    • Mango Indexes: Pass partitioned: true in the index object.
    • Search Indexes: Include options: { partitioned: true } in the design document.
    • MapReduce Views: Include options: { partitioned: true } in the design document.
    // Mango Index
    const i = {
      ddoc: 'partitioned-query',
      index: { fields: ['name'] },
      name: 'name-index',
      partitioned: true,
      type: 'json'
    }
    await db.index(i)
  7. Use Nano with TypeScript

    main

    Nano includes built-in TypeScript definitions. You can import the Nano namespace to type your own classes and handle API responses (like DocumentInsertResponse) with full type safety.

    import * as Nano from 'nano';
    
    let n = Nano('http://127.0.0.1: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;
        }
      }
    }
  8. Get started with Nano

    main

    To use nano, connect it to your CouchDB instance by passing the server URL to the require('nano') function.

    Note: Supplying authentication credentials directly in the URL (e.g., http://admin:password@localhost:5984) is deprecated. Use nano.auth instead.

    Most operations are asynchronous and return native Promises. You can use .then().catch() or the async/await pattern.

    const nano = require('nano')('http://127.0.0.1:5984');
    
    // Create a new database
    await nano.db.create('alice');
    
    // Use an existing database
    const alice = nano.db.use('alice');
    
    // Insert a document
    const response = await alice.insert({ happy: true }, 'rabbit');
  9. Configure Nano connection and URL parsing

    main

    You can configure how Nano connects to your server in several ways:

    1. Standard Connection: Pass the server URL. You then use .use(name) to access specific databases.
    2. Direct Database Connection: If you pass a URL that includes the database name, Nano parses it and returns a database object directly.
    3. Manual Parsing: If your server is behind a proxy or uses rewrite rules, you can set parseUrl: false to prevent Nano from attempting to parse the URL components. You must then use .use(name) to access databases.
    // 1. Standard: returns a server instance
    const nano = require('nano')('http://127.0.0.1:5984');
    const db = nano.use('foo');
    
    // 2. Direct: returns a database instance
    const db = require('nano')('http://127.0.0.1:5984/foo');
    
    // 3. Manual: prevents URL parsing
    const couch = require('nano')({
      url: "http://127.0.0.1:5984/prefix",
      parseUrl: false
    });
    const db = couch.use('foo');
  10. Configure HTTP client options in Nano 11 using agentOptions

    main

    In Nano 11, the requestDefaults option has been removed. To configure connection handling parameters (like timeouts or keep-alive settings), you must now use agentOptions.

    Because Nano 11 uses the native Node.js fetch API, you must provide an instance of an undici.Agent via the undiciOptions key in the Nano configuration object. To do this, you must add undici as a dependency in your own project.

    const agentOptions = {
      bodyTimeout: 30000,
      headersTimeout: 30000,
      keepAliveMaxTimeout: 600000,
      keepAliveTimeout: 30000,
      keepAliveTimeoutThreshold: 1000,
      maxHeaderSize: 16384,
      maxResponseSize: -1,
      pipelining: 6,
      connect: {
        timeout: 10000
      },
      strictContentLength: true,
      connections: null,
      maxRedirections: 0
    }
    const undici = require('undici')
    const undiciOptions = new undici.Agent(agentOptions)
    const nano = Nano({ url: 'http://127.0.0.1:5984', undiciOptions })
  11. Configure logging in Nano

    main

    When instantiating Nano, you can provide a log function to capture requests and responses. You can pass console.log directly or provide a custom function to format the data.

    // Simple logging
    const nano = Nano({ url: process.env.COUCH_URL, log: console.log });
    
    // Custom formatted logging
    const logger = (data) => {
      // data contains err, uri, method, qs, headers, body, etc.
      if (typeof data.err === 'undefined') {
        console.log(data.method, data.uri);
      } else {
        console.log('ERR', data.err.statusCode);
      }
    };
    const nano = Nano({ url: process.env.COUCH_URL, log: logger });