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 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.