Today I decided to give node.js a try on Mac OS X Lion. Having no real hands-on experience in node.js, and wanting to get something end-to-end up and running fast, so I went off in search for a good hands on tutorial. For those of you who don't know what node.js is : "Node.js is a platform built on Chrome's JavaScript runtime for easily building fast, scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices." I stumbled upon a great nodejs intro on github by indexzero. that gives you a nice end-to-end introduction to nodejs and CouchDB. The sample features a REST based api to perform CRUD operations on bookmarks. It uses CouchDB as a datastore. |
The goal of this post is to do the required setup in order to get the sample up and running under 30 minutes. I decided to give it a try and started by installing both node.js and CouchDB on my Mac OS X Lion. By the end of this tutorial, you'll have node.js and CouchDB installed, and will be able to run the REST based API on your OS X installation.
This article assumes you have Git installed on your machine. If you don't have Git installed, checkout the Set Up Git on Mac guide.
Installing node.js and npm
The easiest way to download and install node.js from the the nodejs download section and pickup the Macintosh installer. It will install both node and the node package manager (npm).
After the install, you'll have access to both the node and npm command.
Installing CouchDB
As the node js tutorial uses CouchDB to store objects, we'll need to install CouchDB.
Installing CouchDB is a bit more effort in the sense that we'll download the sources and compile them. Before we can do that, we first need to install Homebrew by executing the following commands :
git clone https://github.com/mxcl/homebrew.git cd homebrew/bin brew install autoconf automake libtool brew install couchdbImportant note ! : There seems to be an issue with the CouchDB recipe introduced a couple of days ago that will prevent you from installing CouchDB. In order to fix this, you'll need to manually edit ~/couch/homebrew/Library/Formula/couchdb.rb
Change
require 'formula' class Couchdb < Formula url 'http://www.apache.org/dyn/closer.cgi?path=couchdb/source/1.1.1/apache-couchdb-1.1.1.tar.gz' homepage "http://couchdb.apache.org/" md5 'cd126219b9cb69a4c521abd6960807a6'
into this (notice how the "source folder" needs to be removed.
require 'formula' class Couchdb < Formula url 'http://www.apache.org/dyn/closer.cgi?path=couchdb/1.1.1/apache-couchdb-1.1.1.tar.gz' homepage "http://couchdb.apache.org/" md5 'cd126219b9cb69a4c521abd6960807a6'If the installation hangs, you can continue CTRL-C the install and try it again by executing
./brew install -v couchdbMore info on the process can be found in "Installing CouchDB on OSX".
After the CouchDB compile is complete, you can start it by executing "./couchdb". You can verify that CouchDB is running by opening a browser and navigating to http://127.0.0.1:5984/_utils.
Downloading the tutorial
Now that we have everything setup, we can continue with the node js intro sample.
We start by checking out the source.
git clone https://github.com/indexzero/nodejs-intro.git
Creating the CouchDB database
Before we can start the tutorial, we're going to have to create a CouchDB database/ We're going to be using the command line (make sure CouchDB is started) :$ curl -X PUT http://127.0.0.1:5984/pinpoint-dev10 {"ok":true}
You can visit the CouchDB Futon http based console on http://127.0.0.1:5984/_utils. You should see your newly created database in the console.
There's an excellent CouchDB guide here.
Starting the tutorial
The node js sample is constructed in a modular way. The lib folder contains several modules that can be bootstrapped using the server script in the bin folder.
For example, to start the CouchDB tutorial, all you need to do is execute the following command from the bin folder
./server -t 02couchdb -s
The -t flag allows you to specify a module from the lib folder, the -s flag will setup the pinpoint-dev database we created earlier.
The sys - util change
Depending on the version of Node that you're using you might see the following error or warning :
$ node -v v0.7.7-pre $ ./server -t 02couchdb -s node.js:247 throw e; // process.nextTick error, or 'error' event on first tick ^ Error: The "sys" module is now called "util". at sys.js:1:69 at NativeModule.compile (node.js:572:5) at Function.require (node.js:540:18) at Function._load (module.js:297:25) at Module.require (module.js:357:17) at require (module.js:373:17) at Object.In order to get rid of the error, you'll need to replace all calls to `require("sys")` with `require("util")` in all the .js files you're using in your project (and its dependent modules).(/home/ubuntu/nodejs-intro/bin/server:3:11) at Module._compile (module.js:444:26) at Object..js (module.js:462:10) at Module.load (module.js:351:32)
Node v0.6.14 doesn't throw an error, but provides a warning that the package has been changed.
$ node -v v0.6.14 $ ./server -t 02couchdb -s The "sys" module is now called "util". It should have a similar interface. Pinpoint demo server listening for 02couchdb on http://127.0.0.1:8000
Running the tutorial
When you run the tutorial, you're going to get some erros
$ ./server 02couchdb The "sys" module is now called "util". It should have a similar interface. node.js:201 throw e; // process.nextTick error, or 'error' event on first tick ^ Error: Cannot find module 'optimist' at Function._resolveFilename (module.js:332:11) at Function._load (module.js:279:25) at Module.require (module.js:354:17) at require (module.js:370:17) at Object.The tutorial has several dependencies that we'll need to download using the Node Package Manager (npm). The npm command comes with the nodejs installation on OS X.(/Users/ddewaele/Projects/Node/nodejs-intro/bin/server:5:12) at Module._compile (module.js:441:26) at Object..js (module.js:459:10) at Module.load (module.js:348:31) at Function._load (module.js:308:12) at Array.0 (module.js:479:10)
Installing node packages
Node packages (dependencies) are installed using the npm like this :
$ npm install optimist npm http GET https://registry.npmjs.org/optimist npm http 200 https://registry.npmjs.org/optimist npm http GET https://registry.npmjs.org/optimist/-/optimist-0.2.8.tgz npm http 200 https://registry.npmjs.org/optimist/-/optimist-0.2.8.tgz npm http GET https://registry.npmjs.org/wordwrap npm http 200 https://registry.npmjs.org/wordwrap npm http GET https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz npm http 200 https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz optimist@0.2.8 ../node_modules/optimist └── wordwrap@0.0.2
The packages will be installed in a node_modules folder in the root folder of our tutorial :
$ ls -l ../node_modules/ total 0 drwxr-xr-x 10 ddewaele staff 340 Apr 1 18:54 optimist
the node js intro requires the following modules to be installed :
npm install winston npm install cradle npm install journey npm install optimist
Running the tutorial
From the bin folder, start the tutorial by executing the following command :
$ ./server -t 02couchdb -s The "sys" module is now called "util". It should have a similar interface. Pinpoint demo server listening for 02couchdb on http://127.0.0.1:8000
Going to http://127.0.0.1:8000/bookmarks in a browser should return the following response :
{"bookmarks":[]}This means our service is up and running. In order to add some data in CouchDB, we're going to be using http-console to access our REST service and send some data over the wire.
Installing http-console
A great tool to help you debug your services is something called http-console. You can install http-console through the node package manager. This time, we'll install the http-console package globally, making it available system-wide. We do this by using the -g (global) switch.
sudo npm install -g http-console
You'l notice that the http-console executable is now available on the command-line. Unfortunately, when starting http-console you'll get the following error.
$ http-console node.js:201 throw e; // process.nextTick error, or 'error' event on first tick ^ Error: require.paths is removed. Use node_modules folders, or the NODE_PATH environment variable instead. at Function.(module.js:378:11) at Object. (/usr/local/lib/node_modules/http-console/bin/http-console:6:8) at Module._compile (module.js:441:26) at Object..js (module.js:459:10) at Module.load (module.js:348:31) at Function._load (module.js:308:12) at Array.0 (module.js:479:10) at EventEmitter._tickCallback (node.js:192:40)
You can fix this by editing the /usr/local/lib/node_modules/http-console/bin/http-console file, and removing the following line :
require.paths.unshift(path.join(__dirname, '..', 'lib'));
After having removed the line above, http console should start. When providing no arguments, it will connect to http://localhost:8080 by default. If you want to connect to another server/port, just specify it as the first argument.
In order to connect to the server provided by the tutorial, we'll start the http-console like this (notice how we use the \json command to set the correct content-type):
$ http-console http://127.0.0.1:8000 The "sys" module is now called "util". It should have a similar interface. > http-console 0.6.1 > Welcome, enter .help if you're lost. > Connecting to 127.0.0.1 on port 8000. http://127.0.0.1:8000/> \json http://127.0.0.1:8000/>
Accessing the REST service
Inside the http-console, executing a GET request is as simple as typing GET /bookmarks. You should get the same response as the one you saw in your browser earlier :
http://127.0.0.1:8000/> GET /bookmarks HTTP/1.1 200 OK Date: Sun, 01 Apr 2012 17:23:27 GMT Server: journey/0.4.0 Content-Type: application/json;charset=utf-8 Content-Length: 16 Connection: keep-alive { bookmarks: [] }
You can also execute POST commands like this by providing a JSON snippet.
http://127.0.0.1:8000/> POST /bookmarks ... { "url": "http://nodejs.org" } HTTP/1.1 200 OK Date: Thu, 05 Apr 2012 11:45:55 GMT Server: journey/0.4.0 Content-Type: application/json;charset=utf-8 Content-Length: 91 Connection: keep-alive { bookmark: { _id: 'WD-G-1', resource: 'Bookmark', url: 'http://nodejs.org' } }
When executing the GET request again, as you can see, our server responds with the bookmark that has now been inserted in CouchDB.
http://127.0.0.1:8000/> GET /bookmarks HTTP/1.1 200 OK Date: Sun, 01 Apr 2012 17:23:27 GMT Server: journey/0.4.0 Content-Type: application/json;charset=utf-8 Content-Length: 16 Connection: keep-alive { bookmarks: [ { _rev: '1-cfced13a45a068e95daa04beff562360', _id: 'WD-G-1', resource: 'Bookmark', url: 'http://nodejs.org' } ] }