April 21, 2012

Module Development with NodeJS Integration

I've been spending some time experimenting with NodeJS in relation to the Drupal environment. In many of these Node/Drupal interactions the NodeJS Integration module has become a key player. There is a very helpful pdf from a talk that beejeebus gave at Drupal camp New Jersey that gives few great visualizations of the underlying principles of how this module works. In essence it "takes care of the plumbing," leaving you, the developer, free to focus on how you would like to integrate. The module is easy to use, and provides a nice set of functions to quickly get rolling. To that point I would like to give a very simple example demonstrating the use of the nodejs integration module.

It's perhaps easiest to think of this integration as allowing Drupal to send messages (data) to a client via NodeJS "plumbing." I feel that the simplest and most transparent example is to allow the user to control what that message is and when it is sent. In this demonstration we build a simple form. This form allows a user to enter a message into a text field. Then on submit, it sends that message to all connected sockets and replaces a bit of <h3> markup within the form. This is very similar to the "broadcast notifications" form included with the NodeJS integration module, but much simpler.

To help visualize this I've setup an install to show this demo in action. If no one else is viewing the demo page while you are, which very likely will be the case, then I encourage you to open another session (open the page in another browser) and arrange the browser windows so you can see them both at once. This way, when you submit in one session, you can see the text in the header change on the other. I've also checked in the code on github to help fill in the gaps from the snippets I provide here, and hopefully help to make sense of my yammering. So with that, lets get into a bit more detail...

Begin with a few considerations.

When you set out to create your module using NodeJS integration it may help to ask yourself some simple questions.

  • Who receives the message?
  • What is the message?
  • How is the message triggered?
  • What will be done with the message once it is received?

Who receives the message?

After a user has requested a connection to socket.io, NodeJS sends this authentication token to Drupal. Drupal then sends back a list of channels the user can "listen" on. To manage which channels a user is assigned to we can use function hook_nodejs_user_channels($auth_user). The $auth_user parameter is the user that is being authenticated. Any checks you might need to perform on the user to determine if they should be assigned a channel can be done here.

In our example we're keeping this really simple. We want to send the message to all users so we give everyone who authenticates the 'swapit_demo' channel.

function swapit_demo_nodejs_user_channels($auth_user) {
  $channels = array('swapit_demo');
  return $channels;

What is the message and how is it triggered?

The message itself is a small object that contains the data it is intended to send, along with some information on how/where to send it. The trigger could really be anything and will likely be pretty obvious based on your use case. In our example module, we're providing the users a form to allow them to write the message that will then be sent on submit. So all of the magic happens in our submit function.

function swapit_demo_swap_submit($form, &$form_state) {
  $user_msg = check_plain($form_state['values']['user_message']);
  //just doing a quick variable set to store the string.
  variable_set('demo_message', $user_msg);
  //broadcasting the string to our listening javascript in waiting
  $message = new stdClass();
  $message->channel = 'swapit_demo';
  $message->broadcast = TRUE;
  $message->data['body'] = $user_msg;
  $message->callback = 'swapIt';

Here we are grabbing the form data from our textfield, and inserting it into a message object. Note how we are setting the channel to the one we set back in hook_nodejs_user_channels. Passing this object to node_send_message will get NodeJS to broadcast to all connected sockets having the 'swapit_demo' channel. The $message->data array contains our user submitted message. The 'body' key is arbitrary.

What will be done with the message?

Now that we have a message being broadcast we need to have a bit of javascript on the client side that will receive and do something with it. In our case, we simply want to replace the text in our given HTML element.

Drupal.Nodejs.callbacks.swapIt = {
  //grab the message and inject into the header
  callback: function (message) {
    if(message.channel == 'swapit_demo') {
      $('form #nodejs-selector').html(message.data.body);

We first define a callback function to receive the message. Note how the callback name is passed to the message object in the submit function in the previous snippet. Our function now verifies the channel on the message object, and uses the message data to replace the text in the #nodejs-selector element. Notice how $message->data['body'] has translated to message.data.body in the javascript. All that remains is to tell the nodejs integration module about the file containing our client side javascript.

function swapit_demo_nodejs_handlers_info() {
  return array(
    drupal_get_path('module', 'swapit_demo').'/swapit_demo.client.js',

That about sums it up in the simplest way I knew how. I welcome any questions, and would love to hear of others experience with this useful module


Ryan Oles theoleschool.com