Picture of a lake in Canada

Getting started with Nodejs, MongoDB and Tower.js

February 22, 2013 , posted under JavaScript nodejs towerjs placement

I have recently purchased a raspberry pi to play with, my primary reason for getting one was to have a lightweight linux server that I could develop for. There has been lots of good things said about NodeJS recently and so I wanted to see what all of the hype was about and try and create a basic chat application.

Most of my time programming has been spent working on .NET MVC apps and so i wanted to choose a framework where I could reuse my knowledge about MVC. After reading the excellent stack overflow post comparing 2 popular node js frameworks Tower.js and Railway.js.

So after having set up node on my Pi (and compiled mongodb which I ended up not using in the end) this is how I started on my mission to create a basic realtime chat application:

Initially I started off by following the brief setup guide on the towerjs github page which got me most of the way there, however I found that when starting it up I recieved an error message saying that the connection to the database failed:

/mnt/piShare/NodeJS/chatApp $ forever server.js
info: socket.io started
Tower development server listening on port 3000

/mnt/piShare/NodeJS/chatApp/node_modules/tower/lib/tower-store/server/mongodb/database.js:36
            throw error;
                  ^
Error: failed to connect to [server.idontexist.com:1337]
_snip_

After having left my Pi to compile MongoDB (Took around 6 hours or so for those interested) I found the Mongo HQ documentation mentioning support for nodejs. The most important section for me at this point in time was the string mentioning how to connect to the database:

//Mongo URL
var MONGOHQ_URL="mongodb://user:[email protected]:port_name/db_name"

This URL fits nicely into the file tower uses to connect to the database (“App\app\config\server\databases.coffee”):

module.exports =
  mongodb:
    development:
        url: 'mongodb://user:[email protected]:port/db'

After having set this up I found that tower started succesfully :) <- That was the easy bit, I found the next steps much more troublesome!

Being a good software developer I added my code to git source control (which proved most useful later!) and then continued on trying to make my application…

I ran a couple of tower generate commands to get my model somewhere close to what I (thought I) wanted: (On reflection I probably should have done this manually to better understand what was going on!)

tower generate scaffold Message belongsTo:user hasOne:channel  body:string
tower generate scaffold User name:string email:string belongsTo:channel
tower generate scaffold Channel hasMany:users hasMany:messages name:string

In my mind this made sense, but unforunatly this is not how relations work in towerjs, after having struggled with this model and a couple of variations (thank goodness for “git reset –hard”!) I decided to contact Lance Pollard (@viatropos) on twitter. He was very helpful and asked me to email/IRC him (due to the time difference I chose email!). He brought my attention to the errors in my inital attempts with the following reasons:

My current models:

class App.Message extends Tower.Model
  @hasOne 'channel'

class App.Channel extends Tower.Model
  @hasMany 'messages'

The reason that this does not work is, a hasOne relationship will look for the id on the associated model, so in my case App.Channel should have a property messageId. Then, the hasMany association will look for the id on the associated model, so App.Message should have a property channelId. So the issue is that the models are looking to each other for the Ids, but neither of them contain them.

class App.Message extends Tower.Model
  @belongsTo 'channel'

class App.Channel extends Tower.Model
  @hasMany 'messages'

This results in the following properties:

 message.get('channelId')

Sure enough these new properties sorted out my issues, my code for the “show” template (This is what I would have called a view coming from .NET) looks like the following at the moment:

@title = "Channel"

partial "flash"

text '{{#with resource}}'
dl class: "content", ->
  dt "Name:"
  dd '{{name}}'  
  dt "Most Recent Message:"
  dd '{{firstMessage}}'
  dt "Messages:"
  text '{{#each allMessages}}'
  dd '{{body}}'
  text '{{/each}}'
text '{{/with}}'

And my “client” controller (Havent worked out why there are controllers for both the client and the server yet):

class App.ChannelsController extends Tower.Controller
  @scope 'all'

  show: ->
    App.Channel.find @params.id, (error, channel) =>
      channel[0].get('messages').desc('createdAt').first (error, message) =>
          channel[0].set("firstMessage", message.get("body"))

      channel[0].get('messages').asc('createdAt').all (error, messages) =>
          channel[0].set("allMessages", messages)
    @render 'show'

  # @todo refactor
  destroy: ->
    @get('resource').destroy()

And then finally the model:

class App.Channel extends Tower.Model
  @field 'name', type: 'String'

  @hasMany 'messages'

  @timestamps()

  firstMessage = "unset"

  allMessages = []

To get around the issue of the messages not having a channelId property when they get created through the application I manually added the object id to a message through MongoHQ’s excellent interface.

It took a couple of attempts to reach the above code (which is no where finished yet!) and I ran into a couple of issues that were unexpected.

  • First off, I expected to be able to do channel.set(..... however it turned out that the “find” function actually returns an array, and so you need to use the indexer (0 for the first item).
  • My next issue was that I recieved an exception when running the show action of the channels controller: assertion failed: Must use Ember.set() to access this property. This is becuase I originally tried to assign the value of firstMessage directly rather than the via the setter function set.
comments powered by Disqus