Adding a Real-Time Module to your Rails API

Wolox Engineering
Wolox
Published in
4 min readJan 5, 2015

Rails is a great framework for building web apps, but what about adding some real-time event logic to your app?

There are different solutions out there that might work for different scenarios. Perhaps the easy-way-out is to go with PubNub, Pusher or another third-party service. You may forget about the infrastructure needed, but of course it comes at a price: you have no control over the core code, you rely on their availability and the pricing can be expensive if you need a lot of connections.

A second solution might be dealing with Rails 4 streaming, which is basically a persistent connection against your Rails app. The main disadvantage is that since Rails is not an event-based server, the code just looks ugly and wrong (a loop basically), and the number of connections might overwhelm your production instance of Puma or Unicorn easily.

So which is the alternative?

Node.js + Redis + Socket.IO

The infrastructure is quite easy to understand: the Node server subscribes to Redis events which are published by Rails server. Clients (browsers) are connected to the Node server, and receive events through that connections (which is usually a WebSocket).

Socket.IO is just a library to handle the reconnections and rely on other transport methods (like polling) if the browser does not support WebSockets. It gives basic functionality like namespaces and channels so that you can easily identify which clients are listening the different events.

There are other alternative libraries like SockJS or Engine.IO (which is what Socket.IO is built upon). You could also go lower level and write your own code for handling the socket connections.

Let’s see some code to understand further how all this components interact with each other. I’ll assume a simple scenario where the event we want to send to the clients is that a new user has joined the platform.

Rails Code

Our awesome Rails server has an API endpoint for creating users, which looks like this:

def create
if User.find_by_email(resource_params[:email]).blank?
@user = User.create!(resource_params)
render json: @user

end

We want to publish to redis an event notifying that the user has been created, I’ll assume there is a REDIS_INSTANCE variable which has been setup in an initiator with the corresponding host and port.

REDIS_INSTANCE.publish ‘user-created’, user: UserSerializer.new(@user).to_json

This will publish the event “user-created” with the user JSON serialization as the body.

Node.js Code

So now it’s time to write the code for our real-time events server which will use Socket.IO to interact with the browser of the clients. I’ll assume you know how to setup a Node.js server (If you don’t, you can read this guide and the official website).
First we need to setup the redis connection:

var redis = require(“redis”).createClient();

This assumes the default redis port and localhost, of course in production, this code needs configuration.
Next we need to subscribe to the redis channels we want to listen to, in this example we just need to subscribe to the “user-created” channel:

redis.subscribe(‘user-created’);

We should also set up the logic for the client connection/disconnection. It can get as simple or complicated as you need, but in our example we’ll just log to the console:

var io = require(‘socket.io’).listen(process.env.PORT || 5001);
io.on(‘connection’, function(socket){
console.log(‘connected socket’)
socket.on(‘disconnect’, function(){
console.log(‘client disconnected’)
socket.disconnect();
});
});

Socket.IO API is really simple though some examples you find out there might not work straight forward since some methods had been changed/removed in the latest versions.

So what is left helps to handle the redis notification when a user is created. We want to broadcast that notification to all the clients connected:

redis.on(‘message’, function(channel, message){
var info = JSON.parse(message);
io.sockets.emit(channel, info);
console.log(‘emit ‘+ channel);
});

Note that this code is generic for any redis message (though we only subscribed to one channel at the moment). It doesn’t do any checking nor transformations, it just emits the same notification to the Socket.IO clients.

Client Code

The client code is pretty easy to setup, first we need to include the Socket.IO client library

<script src=”/socket.io/socket.io.js”></script>

And then we can connect a socket like this:

var socket = io(‘localhost:5001’);

It will use polling until it can establish a connection (note that socket.io handles reconnections).
We are now ready to listen to socket events, it’s as easy as setting up a callback for a specific event name:

socket.on(‘user-created’, function(user){
alert(‘Welcome ’ + user.email);
});

Next Steps

Your app is probably not so simple as this example, and your code will probably need more complex stuff like socket rooms or namespacing (to target specific clients in your Node.js server) and maybe some kind of authentication (to prevent keeping open connections with non-identified users).

To target specific clients, it’s better to read the official rooms and namespaces docs.

To offer some kind of authentication, you can query the DB directly from the Node.js application, or receive a token from the connected socket and query the Rails API instead.

Sources:

socket.io
liamkaufman
tenderlovemaking

Posted by Federico Ramundo(federico.ramundo@wolox.com.ar)

www.wolox.com.ar

--

--