PouchDB Introduction from the beginning
Background
PouchDB is a Javascript implementation of CouchDB. Currently the project is moving rapidly towards an alpha release, but it is already quite usable for small projects. The promise of PouchDB is that it will be the browser-native member of the CouchDB family, bringing along with it Javascript queries, JSON data, and the ability to replicate with any other database in the Couch universe.
When I started working on PouchDB, I was completely unfamiliar with web technologies. Although I have learned a bit since then, I still consider myself to be a novice. Therefore this guide may seem somewhat simplistic to those who have significant web experience. If you fall into this camp, you should check out the PouchDB introduction here.
I ran all of my code on Ubuntu Linux. The only part that might not translate to other environments verbatim is the setup of a local server, but that should not be too challenging to substitute. Enough with the caveats! On to the content.
Setting up a simple page
For the purpose of this tutorial we will create a bare-bones HTML page with a few buttons to expose some of pouch’s functions.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>PouchDB Test</title>
</head>
<input id="enter-text" placeholder="Enter text here" autofocus>
<button id="upload">Upload text</button>
<button id="show">Show all text</button>
<button id="reset">Reset database</button>
<div id="display-area"></div>
</body>
</html>
Make a new folder and save the above in it as index.html
. You should be
able to open index.html
in a web browser and see something like this:
Most of this should be rather self-explanatory. The display-area
will be used later to show the contents of the database.
Adding PouchDB
Create a new folder named js
within the folder you created for the previous step.
The new folder will contain the Javascript code for your page. Now to get
PouchDB itself. The easiest method to acquire the latest version is to
download it directly from PouchDB’s website. Currently,
the download link just gives you the code on a new page. Copy and paste this
into a file named pouchdb.js
in your js
folder. Either the ‘Production’
or ‘Development’ build will work; the ‘Production’ build is just a condensed
version for serving to clients.
Now we need some actual application code to allow the buttons on the html page
to interact with the database. This will be handled in a file appropriately
titled app.js
in the js
folder. Here is the code in its entirety (I
will discuss each section below):
(function() {
'use strict';
var db = null;
var dbname = 'idb://pouch_intro';
window.addEventListener( 'load', loadPouch, false );
function loadPouch() {
Pouch(dbname, function(err, pouchdb){
if(err){
alert("Can't open pouchdb database");
}else{
db = pouchdb;
windowLoadHandler();
}
});
}
function windowLoadHandler() {
//Other logic to be executed when the page loads should be placed here
addEventListeners();
}
function addEventListeners() {
//Hook in to various parts of the page
document.getElementById('upload').addEventListener( 'click', addToDB, false);
document.getElementById('show').addEventListener( 'click', showText, false);
document.getElementById('reset').addEventListener( 'click', reset, false);
}
var reset= function(){
Pouch.destroy(dbname, function(err1){
if(err1){
alert("Database destruction error")
} else {
Pouch(dbname, function(err2, pouchdb){
if(err2){
alert("Database creation error")
} else {
db= pouchdb;
}
})
}
});
}
var addToDB = function(){
var text= document.getElementById('enter-text').value;
db.post({text: text});
}
var showText= function(){
db.allDocs({include_docs: true}, function(err, res){
if(!err){
var out= "";
res.rows.forEach(function(element){
out+= element.doc.text + '<br>';
});
document.getElementById('display-area').innerHTML= out;
}
})
}
})();
This can be broken down into several distinct components. First is the initialization:
var db = null;
var dbname = 'idb://pouch_intro';
window.addEventListener( 'load', loadPouch, false );
function loadPouch() {
Pouch(dbname, function(err, pouchdb){
if(err){
alert("Can't open pouchdb database");
}else{
db = pouchdb;
windowLoadHandler();
}
});
}
The name of the database is up to you. It is important that this name doesn’t conflict
with other databases that the user might have on her computer. One approach
to this might be to prefix the name of your database with the name of your
application. The event listener serves to execute a specific function when
the page loads. That function is loadPouch
, and it opens the database
(first creating it if necessary) before passing control to windowLoadHandler
.
Pouch
is a constructor that creates a database with the specified name and
executes the supplied callback with the new or loaded database as an argument.
Next is the setup:
function windowLoadHandler() {
//Other logic to be executed when the page loads should be placed here
addEventListeners();
}
function addEventListeners() {
//Hook in to various parts of the page
document.getElementById('upload').addEventListener( 'click', addToDB, false);
document.getElementById('show').addEventListener( 'click', showText, false);
document.getElementById('reset').addEventListener( 'click', reset, false);
}
windowLoadHandler
is just an organizational construct to split up tasks
after loading the page. Most likely you will want to do things such as present
a specific message to the user or display stored data. For this example, the
only thing we do is tell the html which Javascript function to call when each
button is pressed.
Third is the reset function:
var reset= function(){
Pouch.destroy(dbname, function(err1){
if(err1){
alert("Database destruction error")
} else {
Pouch(dbname, function(err2, pouchdb){
if(err2){
alert("Database creation error")
} else {
db= pouchdb;
}
})
}
});
}
The reset function combines two useful PouchDB functions. The first is
Pouch.destroy
which removes the previous database. The second is the
constructor which we saw before. Notice that the callback prevents the
constructor from being executed before the database has been successfully
destroyed.
Fourth is the function for adding content to the database:
var addToDB = function(){
var text= document.getElementById('enter-text').value;
db.post({text: text});
}
Perhaps the most important function of PouchDB is saving data. Fortunately
that is very easy to do! Simply post a JSON object and you are good to go.
For this example, the object posted only has a single property, which
contains whatever is in the text box on the html page. Note that updating
documents which already exist is done using db.put
.
Finally we have a function to display the data in the database:
var showText= function(){
db.allDocs({include_docs: true}, function(err, res){
if(!err){
var out= "";
res.rows.forEach(function(element){
out+= element.doc.text + '<br>';
});
document.getElementById('display-area').innerHTML= out;
}
})
}
db.allDocs
produces an object that contains a list of rows. Each row
corresponds to a single document in the database. Every row has three properties:
id
which corresponds to the ‘_id’ field of the document, key
which matches
id
, and value
which contains the current revision number for the
document. If include_docs
is true, each row will also have a doc
property that contains the entire document as found in the database.
Although we used db.allDocs
here, we could have achieved the same effect
with a query:
var showTextAlternative= function(){
var map= function(doc){
if(doc.text){
emit(doc._id, doc.text);
}
};
db.query({map: map}, function(err, res){
if(!err){
var out= "";
res.rows.forEach(function(element){
out+= element.value + '<br>';
});
document.getElementById('display-area').innerHTML= out;
}
})
};
Queries in PouchDB use a simple map-reduce structure to iterate across the
documents in the database and pick out the parts you want. Like before, the include_docs
option gives you the entire matched document, instead of just the properties
included in the emit function. You can also use a reduce function to boil
the results down to a single object. This is useful if you are counting,
averaging, or otherwise aggregating some value. You can find a reference for
map-reduce queries in CouchDB here.
In general, CouchDB references apply to PouchDB as well.
A little bit more about emit
. This is a function that is implicitly
available when using PouchDB. Each call to emit
adds a new key-value pair
(row) to the table of results. The first argument becomes the key of
the pair, and the second argument is its value. The key can be a complex
JSON object, or just a simple integer. In this example we used the _id
property
which every document has. If you create a document using db.post
, _id
is automatically assigned for you (you can set _id
manually using
db.put
). You can learn more about using emit
on the CouchDB wiki.
Now that we have a database and application code, all that remains is to
include the Javascript files in the html page. Replace index.html
with the
following:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>PouchDB Test</title>
</head>
<input id="enter-text" placeholder="Enter text here" autofocus>
<button id="upload">Upload text</button>
<button id="show">Show all text</button>
<button id="reset">Reset database</button>
<div id="display-area"></div>
<script src="js/pouchdb.js"></script>
<script src="js/app.js"></script>
</body>
</html>
The only change is the two <script>
tags at the bottom. You should now have
a page that works like this.
Running a local server
On the command line open the directory that contains index.html
and run this:
python2 -m SimpleHTTPServer 8888
You can now access your page on port 8888. For most applications this is no different than simply opening the page in a web browser, but it is generally preferred to use an actual server because this more closely mimics the web environment.
What’s next?
All of the code in this post can be found here.
In my next post, I will discuss setting up a local instance of CouchDB, syncing with it, and doing the same with a CouchDB instance in the cloud.
blog comments powered by Disqus