Last night I started hacking a chat server using node.js, redis and websockets. It’s a bit of a right of passage for node developers, but I’m actually working towards building an android app I’m tentatively calling riskyclicks. Basically, it’s a public webbrowser, where you can see what pages everyone else in the app is viewing, and if you and another user are on the same domain at the same time, you can chat with them. I thought it up as a nice way to talk to people reading about WWII on wikipedia, or people reading stuff, or people reading about space.

I decided to do it as an android app, because desktop-browser-extension versions of this idea have been done approximately a hundred times, and it never sticks. So I’m gonna learn me some node.js, some redis, some websockets and some android development. (I’m using websockets because it delivers messages all in one, instead of splitting your json packages over multiple packets). It’s going to be totally a rough prototype-app, since I expect no one will actually want to share their browsing history, and I don’t want to spend more than a week or two on the idea.

Callbacks and node.js

Okay, so I started writing my Server class in node.js, and pretty quickly came up against this code:

if msg.action == 'login'
  if @login msg.name, msg.password
    send { action : 'login', success : true }
    # do something here...
  else
    send { action : 'disconnect', reason : 'bad login credentails' }
    ws.close()

So if the server recieves a login action from the client, check the credentials and then log the user in. Except, as soon as I went to implement the Server#login method, I realised I had to query redis, and that wasn’t synchronous, so I couldn’t return true or false from the login method. Instead, I had to use a callback.

This is where I developed something nice I call Success/Failure/Exception. By adding a callback as the last parameter to my login function, I can return the result of the login to the block, and using coffeescript, you can use the nice ? operator to write code that looks like this:

if msg.action == 'login'
  @login msg.name, msg.password, (result) =>
    if result.success?
      send { action : 'login', success : true }
      # do something here...
    else
      send { action : 'disconnect', reason : 'bad login credentails' }
      ws.close()

The login method looks like this:

login: (username, password, callback) ->
  if typeof(username)!='string' or username.length < 3 or username.length > 20 or !username.match /^[a-z0-9]+$/
    return callback Failure "Bad username"

  key = "user:#{username}"

  @redis.get key, (err, reply) ->
    if reply && JSON.parse(reply).password == password
      callback Success
    else
      callback Failure("Unable to find user")

I think this reads quite nicely. You call the callback, and pass it Success or Failure (optionally with a message). I also define Success, Failure and Exception like so:

Success = (message) ->
  @message = message
  @success = true
  @
Success.success = true

Failure = (message) ->
  @message = message
  @failure = true
  @
Failure.failure = true
  
Exception = (message) ->
  @message = message
  @exception = true
  @
Exception.exception = true

This way, you can either do callback(Success) or callback(Success("some message")). It gets a bit gross when you return callback Failure, but apart from that, I think it reads quite nicely, and lets you have success, failure and exception cases returned from your async methods. Next up I’m thinking of adding a production exception logger in the definition of Exception, with a stack trace.