Getting Started with PouchDB Client-Side JavaScript Database

Share this article

Open drawer full of library index cards

This article was peer reviewed by Sebastian Seitz and Taulant Spahiu. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!

Over recent years, client side web applications have gotten more and more sophisticated. Browsers have consistently been providing better JavaScript performance, and are capable of doing more and more things, with rich JavaScript APIs for things like geolocation, and peer-to-peer communication.

The rise of rich web applications also created a need for good client-side storage mechanisms, and that is where JavaScript databases like PouchDB come in.

What is PouchDB?

PouchDB is an open-source JavaScript database inspired by Apache CouchDB that is designed to run well within the browser.

What is a JavaScript database?

In very simple terms, a JavaScript database is a JavaScript object that provides methods (or API) to put, get and search data. In fact, a plain old JavaScript object is the simplest kind of JavaScript database. If you are familiar with Meteor, then you might have heard of Minimongo which is another client side JavaScript database that mimics that MongoDB API.

PouchDB is a JavaScript implementation of CouchDB. Its goal is to emulate the CouchDB API with near-perfect fidelity, while running in the browser or in Node.js.

What makes PouchDB different from databases like Minimongo is that, by default, it is not just in-memory, it uses IndexedDB behind the scenes for its storage. IndexedDB is a low-level API for client-side storage of significant amounts of structured data, including files/blobs. What this means is that PouchDB data is stored on disk, and will be available even after page refresh (however, data stored by one browser will not be available to other browsers).

Different adapters let you change the underlying data storage layer.

Relation to CouchDB

PouchDB is a JavaScript implementation of CouchDB, and emulates it’s API as closely as possible.

In CouchDB, you’d fetch all the documents using this API call

/db/_all_docs?include_docs=true

In PouchDB, it becomes

db.allDocs({include_docs: true})

PouchDB enables applications to store data locally while offline, then synchronize it with CouchDB when the application is back online.

Now, let’s see how you can use PouchDB in your applications.

Installation

To start using PouchDB you just need to include the PouchDB client library. You can use the standalone build, which makes the PouchDB constructor globally available on the window object

<script src="https://cdn.jsdelivr.net/pouchdb/5.4.5/pouchdb.min.js"></script>

or, if you are using it in Node.js/browserify/webpack environment, you can install it with npm.

$ npm install pouchdb --save

Then in your JavaScript:

var PouchDB = require('pouchdb');

(Fun Fact: npm isntall pouchdb also works!)

Working with PouchDB

Creating a database

Creating a PouchDB database is as simple as calling the PouchDB constructor. Let’s create a database called ‘Movies’.

var movies = new PouchDB('Movies');

After running that, you can see basic information about your database, by using the info method, which returns a Promise.

movies
 .info()
 .then(function (info) {
   console.log(info);
 })

The code above outputs the following:

{"doc_count":0,"update_seq":0,"idb_attachment_format":"binary","db_name":"Movies","auto_compaction":false,"adapter":"idb"}

The adapter field indicates that underneath it’s using IndexedDB.

Working with documents

PouchDB is a NoSQL, document-based database, so there is no rigid schema and you can just insert JSON documents directly. Let’s see how you can insert, update, retrieve or delete documents.

Creating a document

You can create a new document using the put method

// returns a promise
db.put(doc, [docId], [docRev], [options])

The params in square brackets are optional. Each document has an _id field associated with it, which works as a unique identifier.

Create a new doc in the previously created Movies database by running the following code:

movies
  .put({
    _id: 'tdkr',
    title: 'The Dark Knight Rises',
    director: 'Christopher Nolan'
  }).then(function (response) {
    console.log("Success", response)
  }).then(function (err) {
    console.log("Error", err)
  })

The response, in case of success, will be something like:

Success {ok: true, id: "tdkr", rev: "3-f8afdea539618c3e8dceb20ba1659d2b"}

Calling movies.info() now will give {doc_count: 1} along with other data indicating that our document has indeed been inserted.

The rev field in the response indicates a revision of document. Each document has a field by the name _rev. Every time a document is updated, the _rev field of document is changed. Each revision point to it’s previous revision. PouchDB maintains a history of each document (much like git).

Read a Document

PouchDB provides a get API method to retrieve a document by its ID. Running:

movies
  .get('tdkr')
  .then(function(doc) {
    console.log(doc)
  })
  .catch(function (err) {
    console.log(err)
  })

will give a response like

{title: "The Dark Knight Rises", director: "Christopher Nolan", _id: "tdkr", _rev: "3-f8afdea539618c3e8dceb20ba1659d2b"}

Update a Document

Let’s say we want to add a ‘year’ field to our document. You’d update the above created doc by running:

movies
  .get('tdkr')
  .then(function(doc) {
    doc.year = "2012"    // new field
    console.log(doc._rev) // doc has a '_rev' field
    return db.put(doc)   // put updated doc, will create new revision
  }).then(function (res) {
    console.log(res)
  })

When updating a document, you must provide a _rev field.

You should see similar output in console:

{ok: true, id: "tdkr", rev: "4-7a34189fb8f2e28fe08b666e699755b8"}

indicating the new revision of document.

Deleting Documents

Deleting a document in PouchDB just sets it’s _deleted property to true. You can call .remove() to do that:

movies
  .get('tdkr')
  .then(function(doc) {
    return movies.remove(doc) // return the promise
  }).then(function(res) {
    console.log("Remove operation response", res)
  })

which is equivalent to doing

movies
  .get('tdkr')
  .then(function (doc) {
    doc._deleted = true
    return db.put(doc)
  })
  .then(...)

Deleting a database

You can delete a database by calling destroy() on the db object.

// returns a promise
movies.destroy() 

Bulk Operations

Until now we have worked with individual documents in PouchDB. It, however, also provides APIs to work with a collection of documents. PouchDB provides two methods for bulk operations – bulkDocs() for bulk writes, and allDocs() for bulk reads.

The bulkDocs() method is pretty simple. It just takes an array of documents that you want to insert into the database.

Insert multiple documents

// Returns a promise
movies.bulkDocs([
  {
    _id: 'easy-a',
    title: "Easy A",
    // other attribues
  },
  {
    _id: 'black-swan',
    title: 'Black Swan',
    // ...
  }
])

Sample Response:

[
  {
    "ok": true,
    "id": "easy-a",
    "rev": "1-84abc2a942007bee7cf55007cba56198"
  },
  {
    "ok": true,
    "id": "black-swan",
    "rev": "1-7b80fc50b6af7a905f368670429a757e"
  }
]

If you want to insert multiple documents, using the bulk API is generally a better way than doing multiple put() requests. Bulk operations tend to be faster than individual operations, because they can be combined into a single transaction (for a local IndexedDB/WebSQL store) or a single HTTP request (for a remote CouchDB server).

Retrieve Multiple Documents

To read multiple docs, PouchDB provides the allDocs() method.

// without {include_docs: true}, only document ids are returned
movies
  .allDocs({include_docs: true})
  .then(function (docs) {
    console.log(docs)
  })

It’s a fast and very useful method. From the PouchDB documentation:

allDocs() is the unsung star of the PouchDB world. It not only returns documents in order – it also allows you to reverse the order, filter by _id, slice and dice using “greater than” and “less than” operations on the _id, and much more.

By default, the documents are returned in ascending _id order. You can specify {descending: true} to reverse the order.

movies
  .allDocs({
    include_docs: true, 
    descending: true
  })
  .then(...)

You can also specify a startkey and endkey parameter to get documents within a range. For example, to get all the movies whose _id starts with ‘a’, or ‘b’, you could run this query:

movies
  .allDocs({
    include_docs: true,
    startkey: 'a',
    endkey: 'c'
  })
  .then(console.log)
  .catch(console.log)

The startKey and endKey parameters are particularly useful for paginated APIs.

Go Realtime with ChangeFeeds

We talked about how PouchDB uses the _rev field to keep track of revisions, with each revision pointing to the previous revision. PouchDB and CouchDB use this chain of revisions for database replication.

However, an implication of this replication algorithm is that it allows you to see the history of the database, letting you answer questions like

  • What changes have been made to the database since a given time?
  • What changes have been made to a particular document?

This is where the changes() API comes in.

To fetch all changes since the beginning of time:

db.changes({
  since: 0,
  include_docs: true
}).then(function (changes) {
  console.log(changes)
}).catch(...)

However, in a web application you are generally more interested in seeing the changes to database that occur after initial page load, so that you can change the UI accordingly. The PouchDB/CouchDB duo have got you covered there also with the live change feed.

db
  .changes({
    since: 'now',
    live: true,
    include_docs: true
  })
  .on('change', function (change) {
    // This is where you can modify UI, based on database change.
    // change.id contains the doc id, change.doc contains the doc
    if (change.deleted) {
      // document was deleted
    } else {
      // document was added/modified
    }
  })
  .on('error', function (err) {
    // handle errors
  })

So, if you had, say, a basic list application, then you could add items in one window, and they’d show up in another window in real-time.

You can see a demo of this in action.

Sync: Take PouchDB Data Beyond Browser

Most apps will need to store data on the back-end and not just in the browser, so you can use PouchDB to save your data locally, but sync it with a back-end CouchDB instance so that the data is available anywhere and not just in that particular browser.

PouchDB provides a very simple API to do this. Assuming, you have a remote CouchDB database set up, syncing it just a matter of two lines of JavaScript.

// local database, that lives in the browser's IndexedDB store
var localDB = new PouchDB('mylocaldb')

// remote CouchDB 
var remoteDB = new PouchDB('http://localhost:5984/myremotedb')

You can replicate local changes to a remote DB by writing

localDB
  .replicate
  .to(remoteDB)
  .on('complete', function () {
    // local changes replicated to remote
  }).on('error', function (err) {
    // error while replicating
  })

However, since many users might have access to same database, it’s more useful to be able to sync changes from a remote DB to the browser. PouchDB has got you covered there also.

The bidirectional sync can be achieved using this one line of JavaScript:

// replicates once
localDB.sync(remoteDB);

Or for live sync:

// keeps syncing changes as they occur
localDB.sync(remoteDB, {live: true})

Next Steps

PouchDB also has a growing ecosystem of plugins and framework adaptors that we don’t have the space to go into here, but are definitely worth checking out. Also, while working with PouchDB, you can use PouchDB Inspector chrome extension, which provides a nice GUI to see your database.

That’s it for this introductory look at PouchDB. It’s definitely one of the more interesting databases out there, and I hope you can see how you could use it to build offline-first, real-time applications.

Frequently Asked Questions about Getting Started with PouchDB

How do I install PouchDB in my project?

To install PouchDB in your project, you need to use npm (Node Package Manager). First, ensure that you have Node.js and npm installed on your system. If not, you can download them from the official Node.js website. Once you have Node.js and npm installed, open your terminal or command prompt and navigate to your project directory. Then, run the following command: npm install pouchdb. This will install PouchDB in your project and you can start using it by requiring it in your JavaScript file like so: var PouchDB = require('pouchdb');.

How do I create a new PouchDB database?

Creating a new PouchDB database is quite straightforward. You just need to call the PouchDB constructor with the name of your database as an argument. Here’s how you do it: var db = new PouchDB('my_database');. This line of code will create a new database named ‘my_database’. If the database already exists, PouchDB will simply open it.

How do I add a document to a PouchDB database?

To add a document to a PouchDB database, you can use the put method. This method takes an object as an argument, which represents the document you want to add. The object must have a _id property, which will be used as the unique identifier for the document. Here’s an example: db.put({_id: 'doc1', name: 'John Doe', age: 30});. This will add a document with an id of ‘doc1’, a name of ‘John Doe’, and an age of 30 to the database.

How do I update a document in a PouchDB database?

Updating a document in a PouchDB database involves two steps. First, you need to fetch the document you want to update using the get method. This method takes the id of the document as an argument and returns a promise that resolves with the document. Once you have the document, you can modify it and then use the put method to save the updated document back to the database. Here’s an example:
db.get('doc1').then(function(doc) {
doc.age = 31;
return db.put(doc);
}).then(function() {
console.log('Document updated successfully');
}).catch(function(err) {
console.log('Error: ', err);
});
This code fetches the document with an id of ‘doc1’, updates its age to 31, and then saves the updated document back to the database.

How do I delete a document from a PouchDB database?

To delete a document from a PouchDB database, you can use the remove method. This method takes the document you want to delete as an argument. However, before you can delete a document, you need to fetch it first using the get method. Here’s an example:
db.get('doc1').then(function(doc) {
return db.remove(doc);
}).then(function() {
console.log('Document deleted successfully');
}).catch(function(err) {
console.log('Error: ', err);
});
This code fetches the document with an id of ‘doc1’ and then deletes it from the database.

How do I fetch all documents from a PouchDB database?

To fetch all documents from a PouchDB database, you can use the allDocs method. This method returns a promise that resolves with an object containing all the documents in the database. Here’s an example:
db.allDocs({include_docs: true}).then(function(result) {
console.log(result.rows);
}).catch(function(err) {
console.log('Error: ', err);
});
This code fetches all documents from the database and logs them to the console.

How do I handle errors in PouchDB?

PouchDB uses promises for all its asynchronous operations, which means you can use the catch method to handle errors. The catch method takes a function as an argument, which will be called if an error occurs. The error will be passed to the function as an argument. Here’s an example:
db.get('doc1').catch(function(err) {
console.log('Error: ', err);
});
This code tries to fetch a document with an id of ‘doc1’. If an error occurs (for example, if the document does not exist), the error will be logged to the console.

How do I sync a PouchDB database with a remote CouchDB database?

PouchDB provides a sync method that you can use to synchronize a local PouchDB database with a remote CouchDB database. The sync method takes the URL of the remote database as an argument and returns a promise that resolves when the synchronization is complete. Here’s an example:
PouchDB.sync('my_database', 'http://localhost:5984/my_remote_database').then(function() {
console.log('Sync complete');
}).catch(function(err) {
console.log('Error: ', err);
});
This code synchronizes a local database named ‘my_database’ with a remote database located at ‘http://localhost:5984/my_remote_database‘.

How do I use PouchDB in a browser?

PouchDB is designed to work in both Node.js and browser environments. To use PouchDB in a browser, you need to include the PouchDB script in your HTML file. You can download the script from the PouchDB website or use a CDN. Here’s how you include the script using a CDN:
<script src="https://cdn.jsdelivr.net/npm/pouchdb@7.2.2/dist/pouchdb.min.js"></script>
Once the script is included, you can start using PouchDB in your JavaScript code.

How do I debug PouchDB?

PouchDB provides a debugging tool that you can use to log PouchDB operations to the console. To enable debugging, you need to call the debug.enable method with the name of the PouchDB module you want to debug as an argument. Here’s an example:
PouchDB.debug.enable('pouchdb:api');
This code enables debugging for the ‘pouchdb:api’ module, which logs all PouchDB API calls to the console. To disable debugging, you can call the debug.disable method.

Jatin ShridharJatin Shridhar
View Author

Jatin is an existing software dev, and aspiring stand up comic. Previously he has worked for Amazon. For a long time, he thought that .js referred to his initials.

databasesnilsonjnosqlPouchDB
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week