Thursday 15 August 2013

Basic web storage


Node.js + Coffee + mongoDB


Good morning boys and girls, today I would like to share with you a little something I've been working on. 

So I set out to build a web service that would 
  1. Read in a POST request on a Node.js server and save it to a mongo database 
  2. When a GET request comes in, return all the posted data
    (this is the normal type of message you receive from a browser. i.e. get me this page/image/thing..).
and for good measure let's make sure we're using coffeescript's class ability.

To get started you will need to install the mongoDB server.

There are very good step-by-step tutorials for all major platforms on the mongoDB site. so once you Install MongoDB. Fire it up to make sure everything is working fine.
navigate to wear the mongoDB executable is
cd /mongodb/bin

Now start you mongoDB server *By default, MongoDB stores data in the /data/db directory.
mongod

Output
Thu Aug 15 13:21:05.023 [initandlisten] MongoDB starting : pid=7444 port=27017 dbpath=\data\db\ 64-bit host=blackbolt
Thu Aug 15 13:21:05.025 [initandlisten] db version v2.4.4
Thu Aug 15 13:21:05.025 [initandlisten] git version: 4ec1fb96702c9d4c57b1e06dd34eb73a16e407d2
Thu Aug 15 13:21:05.026 [initandlisten] build info: windows sys.getwindowsversion(major=6, minor=1, build=7601, platform=2, service_pack='Service Pack 1') BOOST_LIB_VERSION=1_49
Thu Aug 15 13:21:05.027 [initandlisten] allocator: system
Thu Aug 15 13:21:05.028 [initandlisten] options: {}
Thu Aug 15 13:21:05.079 [initandlisten] journal dir=\data\db\journal
Thu Aug 15 13:21:05.081 [initandlisten] recover begin
Thu Aug 15 13:21:05.082 [initandlisten] recover lsn: 15263608
Thu Aug 15 13:21:05.083 [initandlisten] recover \data\db\journal\j._0
Thu Aug 15 13:21:05.085 [initandlisten] recover skipping application of section
seq:0 < lsn:15263608
Thu Aug 15 13:21:05.086 [initandlisten] recover skipping application of section
more...
Thu Aug 15 13:21:05.164 [initandlisten] recover cleaning up
Thu Aug 15 13:21:05.165 [initandlisten] removeJournalFiles
Thu Aug 15 13:21:05.167 [initandlisten] recover done
Thu Aug 15 13:21:05.332 [initandlisten] waiting for connections on port 27017
Thu Aug 15 13:21:05.332 [websvr] admin web console waiting for connections on port 28017


We can now just leave this running.

Now in a new terminal window we install the mongoDB driver for Node.js
npm install mongodb

So here we are going to have 2 files "server.coffee" and "mongo.coffee"

File: mongo.coffee
Here we have our class and the constructor. The constructors doing two things
  1. The 'response' being passed in is prefixed with '@' so it automatically becomes an attribute of the class.
  2. Creating our mongoDB connection

class myMongo
 constructor: (@response)->
  databaseUrl = "mydb"
  collections = ["randomValues"]
  @db = require("mongojs").connect(databaseUrl, collections)

Here we create the save function that is used for the post messages.
It's split into two functions, so "save" initiates the writing to database and "_saveCallBack" after the values have been stored.
*note: there 'saveCallBack' function starts with an underscore. This is to denote that the function is private

 
 save: (args) =>
  @db.randomValues.save(args, @_saveCallBack)
 
 _saveCallBack: (err, saved) =>
  if err?
   console.log(err)
   @response.write(err)
  else
   console.log("Saved #{JSON.stringify(saved)}")
   @response.write("will be saved")
  @response.end()

Here is a similar setup to "save" in that it has two functions but of course we are reading out the information that has been stored by the POST messages. You should know that line 13 is where the magic happens as it loops thru the return values outputting each on a new line("\n")

 
 find: =>
  @db.randomValues.find {}, @_findCallBack
 
 _findCallBack: (err, values) =>
 
  console.log "#{values.length} Requested"
  
  if err? 
   console.log err
  else if values.length is 0
   @response.write "No values found"
  else
   @response.write JSON.stringify(val)+"\n" for val in values
  @response.end()

Finally we use "export" to allow our mongoDB manager class to be used with other files.
module.exports = myMongo


File: server.coffee
Very simple to start off. I'm bringing in the HTTP module and the mongoDB source that will handle the reading and writing of our values.
http = require "http"
myMongo = require "./mongo"

Here's our request functional that will be run every time there is a connection is made.

There are four main thing happening here
  1. Set our HTTP header
  2. Created an instance of our mongoDB manager (mongo.coffee)
  3. Check if it a POST message and pass the values to be saved
  4. Else if it's a GET message and get the mongoDB manager to return all stored values

onRequest = (request, response) ->

 response.writeHead 200,
  "Content-Type": "text/plain"

 #pass in the 'response' object, so the mongoDB manager 
 #can use it to output the values on a GET   
 mongoConnet = new myMongo(response)

 if(request.method is 'POST')
  body = '';
  request.on 'data',  (data) ->
   body += data

  request.on 'end', () ->
   POST =  JSON.parse (body)
   mongoConnet.save(POST)

 else if(request.method is 'GET')
  mongoConnet.find()

Here is where we build our server. you can see we're passing in the "onRequest" function and listening on PORT:8888.
O, and a little message to let us know our server is up and running.
server = http.createServer()
server.on("request", onRequest)
server.listen(8888)

console.log "Server up and Ready to eat"

Now here comes two commands and you can run them in any order and see what you get. :D

This first one is the POST message that will store information into our database.
curl -i -X POST -H "Content-Type: application/json" -d '{"name":"brian","code":"sandwich"}' localhost:8888

Next we have the GET message that will retrieve our stored values.
curl -i localhost:8888

a copy of both source files is available on: GITHUB

... continue reading!