I am very excited about the RabbitMQ & Node.js fucntionality. I got the ball rolling with a Redis/Sinatra tutorial, and now I want to show you how to get started with RabbitMQ in a basic Node.js app.

I picked Node for this app because I’ve been pretty heavily embedded in Node-world recently, but I would recommend using RabbitMQ in conjunction with the many clients that exist in a variety of languages and frameworks. This tutorial will cover only the very basics.

Getting started: dependencies and server setup

First things first, make sure that you have RabbitMQ installed on your machine.

I’m of the opinion that getting dependencies right can save you from a lot of headaches down the road, I always start a new Node app with my package.json file. In this example, I’ll be using an Express server (still the standard for Node, and with good reason) and the amqp module as my RabbitMQ client. Here’s what my package.json file will look like:


{
  “name”: “af-rabbitmq-example”,
  “version”: “0.0.1”,
  “scripts”: {
  “start”: “node app”
},
  “dependencies”: {
    “express”: “*”,
    “amqp”: “*”
  }
}

Now, I’ll run the usual npm install and won’t have to fuss with dependencies any longer. If you’re making a simple app and you know in advance what your dependencies will end up being, I highly recommend doing things this way. Now, I’ll create a server.js file that will house the entirety of my app. I’ll start with declaring my dependencies at the top and setting up my server:
    
var express = require('express'),
http = require('http'),
path = require('path'),
amqp = require('amqp');

var app = express();

http.createServer(app).listen(app.get('port'), function(){
  console.log(“RabbitMQ + Node.js app running on AppFog!”);
});

    

Now, we need to configure our server to perform some of the actions that we’ll need later. I’ll essentially just stick with some of Express’s defaults. Here’s what my app configuration looks like:

    
app.configure(function(){
  app.set('port', process.env.PORT || 3000);
  app.set('views', __dirname + '/views');
  app.set('view engine', 'jade');
  app.use(express.bodyParser());
  app.use(express.static(path.join(__dirname, 'public')));
});
    

I’ll be using the Jade templating framework for my HTML templates (I’ll only have two templates) and I’ll need the Express bodyParser for form input later on.

I also want to set up a few default values prior to setting up our routes. I need to set default connection statuses for my overall RabbitMQ server connection, the connection to my message exchange, and the connection to my messaging queue:

    
app.connectionStatus = 'No server connection';
app.exchangeStatus = 'No exchange established';
app.queueStatus = 'No queue established';
    

These values will give us feedback as to the state of our connection. Later on, these values will be overridden when our RabbitMQ connection is appropriately set up.

Our main page

Most of the action in our app will happen on the main page and through our basic get('/') route. Let’s begin setting up our index.jade HTML template first:

    
doctype 5
html
head
  title= title
  link(rel='stylesheet', href='style.css')
body
h1= title
    

So far, nothing but a title header, but soon we’ll be adding more goodies to the main page. Now, let’s set up our '/' route in our server.js file:

    
app.get('/', function(req, res){
  res.render('index.jade',
    {
      title: 'Welcome to RabbitMQ and Node/Express on AppFog',
      connectionStatus: app.connectionStatus,
      exchangeStatus: app.exchangeStatus,
      queueStatus: app.queueStatus
    });
});
    

Now, when I go to the main '/' directory, my server will render our index.jade file and will use the JSON hash I added to the render function to input data into the HTML template. As it stands, it will send my default connection statuses back to the client. If we try to run that now, however, we’ll get an error because we need to put that information somewhere in our HTML template. Let’s do that now, underneath the h1= title:

    
h3 The connection status is currently:

p= connectionStatus
p= exchangeStatus
p= queueStatus
    

Now, the respective statuses of our RabbitMQ connections will show up on the main page. For now, they will all display the default because our RabbitMQ is completely dormant. Time to switch it on.

Putting RabbitMQ to work

You’ll notice that I haven’t actually done anything with RabbitMQ yet. This is by design. I want to get our RabbitMQ connection started only in response to an HTTP request. To that end, I’m going to set up a start-server route that will accomplish precisely that and then change our server status on the main page:

    
app.post('/start-server', function(req, res){
  app.rabbitMqConnection = amqp.createConnection({ host: 'localhost' });
  app.rabbitMqConnection.on('ready', function(){
  app.connectionStatus = 'Connected!';
  res.redirect('/');
});
    

Now, if I post a /start-server request, then a connection to the RabbitMQ server will be established. If you’re in your local environment, you will want to simply set the host to 'localhost' for experimental purposes. If you want to deploy on AppFog, use the setup from above.

Now, let’s go back to our index.jade page and insert some buttons so that users can make the appropriate HTTP requests set up in our server:

p Please select an option:

    
form(action='/start-server', method='post')
  input(type='submit', value='Start the server')
form(action='/new-exchange', method='post')
  input(type='submit', value='Establish an exchange')
form(action='/new-queue', value='Establish a message queue')
    

This will set up three buttons on our main page. We’ll set up routes for the second and third button in the next section. For now, I’m going to do one last thing. At the bottom of the page, I’m going to insert an if clause (one of the nice features of Jade) that will make an announcement show up on the page that my message server is all set up and ready to go.

    
if (queueStatus == 'The queue is ready for use!' && exchangeStatus == 'An exchange has been established!')
  h2 BOOM! SUCCESS!
  a(href='/message-service') Click here to go to the messaging service!
      

So that’s it for our main landing page. Now, let’s get back to our router and bake some more RabbitMQ functionality in.

Making a new exchange and a new queue in response to HTTP requests

Creating our message exchange and our queue will look a lot like setting up our initial server connection, but with some important differences that you’ll see in a moment. First, our exchange:

    
app.post('/new-exchange', function(req, res){
  app.e = app.rabbitMqConnection.exchange('test-exchange');
  app.exchangeStatus = 'The queue is ready to use!';
  res.redirect('/');
});
      

Now, when you click on the “Establish an exchange” button, you will create a new RabbitMQ exchange as a global variable app.e, change the status of our exchange, and redirect back to the main page. So far, so good. Now for our message queue:

    
app.post('/new-queue', function(req, res){
  app.q = app.rabbitMqConnection.queue('test-queue');
  app.queueStatus = 'The queue is ready for use!';
  res.redirect('/');
});
      

Now, we’ll be able to click all three buttons on the main page and thereby set up our server, exchange, and queue. Once we’ve done so, this will appear on the page at the bottom:


BOOM! Success!
Click here to go to the messaging service!

If we click on “here”, this will take us to the /message-service page. I’m going to set things up so that our message queue and exchange are bound to one another when we go to the /message-service page. I’ll make that part of of my GET route for the page:
    
app.get('/message-service', function(req, res){
  app.q.bind(app.e, '#');
  res.render('message-service.jade',
    {
      title: 'Welcome to the messaging service',
      sentMessage: ''
    });
});
      

So when we click on the link to go to the /message-service page, two things will happen. First, we’ll bind our app.q message queue to our app.e message exchange. The bind function takes two arguments. The first is the exchange to which we’re binding our queue (in this case the only exchange that we’ve set up), and the second is being listened for. In our case, we’re going to input #, which means that we’ll be listening for anything and everything that comes down the pike.

At this point, if you can get to the message service page, that means that everything in RabbitMQ is fully set up behind the scenes. But we can’t get there just yet because we still need to make our HTML template and set up our server.

Setting up our hyper-basic messaging service

If we click on the link to the /message-service page, it will do nothing. Why? Because we’ve set up neither the route nor the actual HTML template for it. Let’s start with the message-service.jade file. I’ll use the same headers and what-not from before (all the way down through the h1= title line), and insert the following below that:

    
h3 Please type in a message below:
form(action='/newMessage', method='post')
  input(type='text', value='newMessage')
  input(type='submit', value='Submit new message')

h3 Here is the message you typed:
p= sentMessage
      

What’s going here is simpler than it may look. This is setting a form where we can input text, and down below, that message will be displayed under the bottom header. The kicker here is that when we input a message in the form, it will be passed into our message exchange, then to our queue, then back to our server, and then onto the page, completing the loop that we set up initially.

So now that our /message-service page template has been put together and we have a functioning page to go to once our RabbitMQ circuitry has been pieced together, we need to set up our server to handle the message that we input. Let’s set up a new POST route in our server.js file:

    
app.post('/newMessage', function(req, res){
  var newMessage = req.body.newMessage;
  app.e.publish('routingKey', { message: newMessage });

  app.q.subscribe(function(msg){
  res.render('message-service.jade',
    {
      title: 'You\'ve got mail!',
      sentMessage: msg.message
    });
  });
});
      

This is the trickiest thing we’ve done so far, so let’s walk through the steps one by one. First, we need to create a newMessage variable out of the data that we’ve inserted into the form. Express’s bodyParser enables us to do this easily. Next, we need to pass this message into our message exchange by running an app.e.publish function. For our function arguments, we have to tell RabbitMQ what is being sent and what the content of the message is. Here, we’ll be sending a routing key and a message as a one-item hash (that is, as a JavaScript object). The content of that message will be the newMessage that we extracted from our form input.

So now we have a message published to the exchange. Now, we need our message queue to subscribe to the exchange. And so we run a simple app.q.subscribe function and pass in an anonymous function as the argument. That function will take any message that comes its way as an argument. What I will do with that message is simple. I’ll pass it along into the message-service.jade template, and it will be displayed when the page is re-rendered. The msg.message variable will take the value of the message key in the hash that I sent through the RabbitMQ pipeline and, if all goes well, will show up under the “Here is the message you typed” heading.

Node.js + Express + RabbitMQ message service: done

As I’m sure you’ve noticed, this was a pretty circuitous route for this message to take. But I hope that this tutorial gives you a good set of bearings in three things:

(1) The basics of RabbitMQ, including setting up a server, queue, and exchange.

(2) Using RabbitMQ in a highly HTTP-driven fashion.

(3) Getting a head start with RabbitMQ in conjunction with Node and Express.

I also have to admit that this was by far the hardest tutorial I’ve ever done in terms of my own efforts. RabbitMQ was a tough nut for me to crack. But perseverance paid off, and once a few initial conceptual hurdles were overcome I began to really see the power of AMQP and to grasp things on a very intuitive level.

I strongly suggest that you commit yourself to the same journey.