Permalink
Browse files

Updated the bogart-couchdb article

  • Loading branch information...
1 parent d86d56b commit d3d60d44aefa74622e390dd6340f1ba2d290ad37 @nrstott nrstott committed Sep 15, 2011
View
170 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
@@ -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)
@@ -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:
@@ -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
@@ -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>
View
66 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);
View
47 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>
View
16 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>
View
25 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.