Skip to content

Commit

Permalink
Updated the bogart-couchdb article
Browse files Browse the repository at this point in the history
  • Loading branch information
nrstott committed Sep 15, 2011
1 parent d86d56b commit d3d60d4
Show file tree
Hide file tree
Showing 5 changed files with 307 additions and 17 deletions.
170 changes: 153 additions & 17 deletions articles/bogart-couchdb.markdown
@@ -1,4 +1,4 @@
Title: Blog with CouchDB, Bogart, and Node.js
Title: A Simple Blog with CouchDB, Bogart, and Node.js
Author: Nathan Stott
Date: Wed Sept 14 2011 09:50:00 GMT+0000 (UTC)
Node: v0.4.11
Expand All @@ -8,34 +8,58 @@ blogging engine.

## Pre-Requisites

### npm ###

[npm](http://github.com/isaacs/npm) is the most popular package manager for Node.js.
Installing npm is easy.

curl http://npmjs.org/install.sh | sh

A note for windows users: npm does not currently work on windows. It will in the future.

### Bogart ###

Bogart is a Sinatra-like framework designed to make it easy to create JSGI compliant
web applications on node.js.
[Bogart](http://github.com/nrstott/bogart) is a Sinatra-like framework designed
to make it easy to create JSGI compliant web applications for node.js.

Bogart is in the npm registry.

npm install bogart

### CouchDB ###

CouchDB is a document-oriented database with a RESTful interface. CouchDB works well
with JavaScript since CouchDB speaks JSON. Also, CouchDB is queried using 'views' that are,
by default, written in JavaScript. [Download the latest release][http://couchdb.apache.org/downloads.html]
from here. CouchBase also maintains [debian and rpm packages][http://www.couchbase.com/downloads]
[CouchDB](http://couchdb.apache.org) is a document-oriented database with
a RESTful interface. CouchDB works well with JavaScript since CouchDB speaks JSON.
Also, CouchDB is queried using 'views' that are, by default, written in JavaScript.
[Download the latest release](http://couchdb.apache.org/downloads.html) from here.
CouchBase also maintains [debian and rpm packages](http://www.couchbase.com/downloads)
for the community.

## JSGI

Bogart is a JSGI-based framework. JSGI is specified by the CommonJS mailing list. Knowledge
of JSGI is helpful when dealing with Bogart; however, it is not necesarry. You can find
[more information about JSGI][http://wiki.commonjs.org/wiki/JSGI] on the
[CommonJS wiki][http://wiki.commonjs.org].
[more information about JSGI](http://wiki.commonjs.org/wiki/JSGI) on the
[CommonJS wiki](http://wiki.commonjs.org).

## Mustache

[Mustache](http://github.com/janl/mustache.js) is a minimal templating engine
with {{mustaches}}. Mustache is the default templating engine of Bogart.
When you install Bogart, you will also be installing Mustache.

## CouchDB-CommonJS

[CouchDB-CommonJS](http://github.com/nrstott/couchdb-commonjs) is a promise-based
CouchDB library available in the npm registry. It can also be used in the browser
or with Narhwal.

npm install couchdb

## What will our application do?

To keep things simple, we're going to only tackle the most basic of functionality. We will
support the following methods:
To keep things simple, we're going to only tackle basic functionality. Our blog
application will support the following methods:

* Create a new post (POST /posts)
* Show a list of all the posts (GET /posts)
Expand All @@ -45,7 +69,7 @@ support the following methods:
## Lets get startred!

A Bogart application consists of a JSGI server with one or more pieces of middleware and
one or more Bogart routers.
one or more Bogart routers each containing any number of routes.

The canonical 'Hello World' application in Bogart can be written as follows:

Expand All @@ -61,12 +85,12 @@ To run this program, first execute the following commands to setup your blog dir
npm install bogart

This will create a new directory named bogart-couchdb-blog and install bogart to the
node_modules subdirectory of this directory. Next, copy the JavaScript into a file in
bogart-couchdb-blog named `app.js` and then execute
node_modules subdirectory of this directory. Next, copy the JavaScript into a file into
bogart-couchdb-blog and name it `hello-world.js` and then execute

node app.js
node hello-world.js

Visit [http://localhost:8080][http://localhost:8080] in your browser.
Visit [http://localhost:8080](http://localhost:8080) in your browser.

## Creating the package.json file

Expand Down Expand Up @@ -138,6 +162,118 @@ We will make a small change to our application to add the `ParseForm` middleware
app = bogart.middleware.ParseForm(app);
bogart.start(app);

Lets take a side-step to discuss how we can work with CouchDB using the `couchdb` package
from the npm registry.

At the top of our `app.js` add the following:

var couchdb = require('couchdb');

In the closure that configures Bogart routes, create a couchdb client and a database representation.

var app = bogart.router(function(get, post, put, destroy) {

var client = couchdb.createClient(5984, '127.0.0.1', { user: 'myuser', password: 'mypass' })
, db = client.db('blog')
, viewEngine = bogart.viewEngine('mustache');
// configure routes...
});

This creates a couchdb client connecting to '127.0.0.1' and port 5984. If your CouchDB is in
Admin Party, you do not need to supply the user and password in an options hash. If you have a
CouchDB users setup, please provide your username and password.

Now we will create a route to handle the `POST` of our form.

post('/posts', function(req) {
var post = req.params;
post.type = 'post';

return db.saveDoc.then(function(resp) {
return bogart.redirect('/posts');
});
});

We add the `type` attribute to the `post` so that as we add more document types in the future,
we can easily create CouchDB views to find only specific document types. This is not a built-in
CouchDB concept. It is a useful convention that makes creating views simpler.

## Adding a CouchDB view to retrieve posts

CouchDB is queried using [map/reduce views](http://wiki.apache.org/couchdb/HTTP_view_API)
that are defined on design documents. This means that we need to create a design
document before we can query a list of the posts in our database.

Lets create a JavaScript file `syncDesignDoc.js` in the `lib` directory of our project.

<bogart-couchdb/syncDesignDoc.js>

Execute `node lib/syncDesignDoc.js` to update the database with the latest design document.

## Listing Posts

Lets create a Mustache template to list the posts from our database.

<bogart-couchdb/posts.html>

Next, lets create a Bogart route to render this template. We will query the database using
`db.view`, process the response from CouchDB, and respond with the rendered template. Bogart
makes this easy:

get('/posts', function(req) {

return db.view('blog', 'posts_by_date').then(function(resp) {
var posts = resp.rows.map(function(x) { return x.value; });
return viewEngine.respond('posts.html', {
locals: {
posts: posts
}
});
});
});

## Show an Individaul Post

It's time to create a route to show an individual post. This page will also contain
a form for adding comments.

The template will be as follows:

<bogart-couchdb/post.html>

The Bogart route to display this is as simple as the route to display the form
for creating new posts.

get('/posts/:id', function(req) {
return db.openDoc(req.params.id).then(function(post) {
return viewEngine.respond('post.html', { locals: post });
});
});

The route to accept the `POST` from the comments form is similar to the route to accept
a new blog post:

post('/posts/:id/comments', function(req) {
var comment = req.params;

return db.openDoc(req.params.id).then(function(post) {
post.comments = post.comments || [];
post.comments.push(comment);

return db.saveDoc(post).then(function(resp) {
return bogart.redirect('/posts/'+req.params.id);
});
});
});

## Summing it up

As you can see, getting started with Bogart and CouchDB is simple. We are a long way from
having a full-featured blog, but hopefully this will inspire some out there to try
working with Node.JS, Bogart, and CouchDB!

The full source code of the finished `app.js` is below:

## Adding a CouchDB view to retrieve posts
<bogart-couchdb/app.js>
66 changes: 66 additions & 0 deletions articles/bogart-couchdb/app.js
@@ -0,0 +1,66 @@
var bogart = require('bogart');
var couchdb = require('couchdb');

var app = bogart.router(function(get, post, put, destroy) {

var client = couchdb.createClient(5984, '127.0.0.1', { user: 'nathan', password: 's4stott' })
, db = client.db('blog')
, viewEngine = bogart.viewEngine('mustache');

get('/posts', function(req) {

return db.view('blog', 'posts_by_date').then(function(resp) {
var posts = resp.rows.map(function(x) { return x.value; });

return viewEngine.respond('posts.html', {
locals: {
posts: posts,
title: 'Blog Home'
}
});
});
});

get('/posts/new', function(req) {
return viewEngine.respond('new-post.html', {
locals: {
title: 'New Post'
}
});
});

post('/posts', function(req) {
var post = req.params;
post.type = 'post';

return db.saveDoc(post).then(function(resp) {
return bogart.redirect('/posts');
});
});

get('/posts/:id', function(req) {
return db.openDoc(req.params.id).then(function(post) {
return viewEngine.respond('post.html', { locals: post });
});
});

post('/posts/:id/comments', function(req) {
var comment = req.params;

return db.openDoc(req.params.id).then(function(post) {
post.comments = post.comments || [];
post.comments.push(comment);

return db.saveDoc(post).then(function(resp) {
return bogart.redirect('/posts/'+req.params.id);
});
});
});
});

// Add ParseForm middleware to automatically process the parameters
// of form submissions into a JSON object that is easily usable (req.body, req.params).
app = bogart.middleware.ParseForm(app);

// Start the JSGI server.
bogart.start(app);
47 changes: 47 additions & 0 deletions articles/bogart-couchdb/post.html
@@ -0,0 +1,47 @@
<a href='/posts'>Home</a>

<h1>{{title}}</h1>

<div class='post-content'>
{{body}}
</div>

<div>
<h2>Comments</h2>
<ul id='comments'>
{{#comments}}
<li>
<div>
Author: <span class='author'>{{author}}</span>
</div>
<div>
{{body}}
</div>
</li>
{{/comments}}

{{^comments}}
No Comments Yet
{{/comments}}
</ul>
</div>

<form method='post' action='/posts/{{_id}}/comments'>
<fieldset>
<legend>Leave a Comment</legend>

<div>
<label for='author'>Your Name</label>
<input name='author' />
</div>

<div>
<label for='body'>Your Comment</label>
<input name='body' />
</div>

<div class='buttons'>
<input type='submit' value='Post Comment' />
</div>
</fieldset>
</form>
16 changes: 16 additions & 0 deletions articles/bogart-couchdb/posts.html
@@ -0,0 +1,16 @@
<h2>Posts</h2>

<a href='/posts/new'>New Post</a>

<ul>
{{#posts}}
<li>
<div class='post'>
<h2><a href='/posts/{{_id}}'>{{title}}</a></h2>
<p>
{{body}}
</p>
</div>
</li>
{{/posts}}
</ul>
25 changes: 25 additions & 0 deletions articles/bogart-couchdb/syncDesignDoc.js
@@ -0,0 +1,25 @@
var couchdb = require('couchdb');
var client = couchdb.createClient(5984, '127.0.0.1', { user: 'nathan', password: 's4stott' });
var db = client.db('blog');

var designDoc = {
_id: '_design/blog',

language: 'javascript',

views: {
'posts_by_date': {
map: function(doc) {
if (doc.type === 'post') {
emit(doc.postedAt, doc);
}
}.toString()
}
}
};

db.saveDoc(designDoc).then(function(resp) {
console.log('updated design doc!');
}, function(err) {
console.log('error updating design doc: '+require('util').inspect(err));
});

0 comments on commit d3d60d4

Please sign in to comment.