August 04, 2016

How to Build Real-time Applications Using Node.js and RethinkDB

by Jscrambler

How to Build Real-time Applications Using Node.js and RethinkDB

About RethinkDB

If you need a NoSQL database which work with JSON data, has full support for realtime searching and has a mix of paradigms between SQL and NoSQL, a good option is RethinkDB.

This is a open-source database, which all JSON’s data is persisted into tables like a conventional SQL database, allowing you to run queries among multiple tables using the classic join command. But you can also persist arrays and sub-documents like you are used to do it in MongoDB, CouchDB or PostgreSQL.

There are some cool stuffs from RethinkDB like:

  • GeoSpartial support;
  • API to handle with Strings, Dates, Booleans and Documents;
  • Math API;
  • Map-reduce support;
  • HTTP client to catch some external data;
  • Changefeeds which is a realtime searching;
  • Index support (simple, compound and multi);
  • Native web admin dashboard;

Protect Your Node.js App

Building the application

What about building something useful using RethinkDB? To explore the realtime searching, let’s build a simple global timeline using the changefeed feature to list all data in the timeline in realtime, by using Node.js, Express, Socket.IO and RethinkDB.

First, you need to install RethinkDB server, before you start writing the codes below, to install this database I recommend you to read and follow the instruction of this link http://rethinkdb.com/docs/install according to your operation system.

After you install it, run this commands below to initiate the project:

mkdir timeline  
cd timeline  
npm init  
npm install--save express socket.io rethinkdb  

Now let’s work! To simplify things we are going to use ES6 code native from Node v6.x.x version and the backend will be a single file code for study purposes, but if you need to build a complex and well structure backend server using RethinkDB, you can see this project node-api-examples which has a list some API’s examples using some web routers and databases, there are some examples using RethinkDB with Koa, Express and Hapi.js.

Well, let’s write the backend server of our application, you can create the file index.js using the code below:

const http = require('http');  
const fs = require('fs');  
const express = require('express');  
const socketIO = require('socket.io');  
const r = require('rethinkdb');  
const config = require('./config.json');

// Loading Express, HTTP, Socket.IO and RethinkDB
const db = Object.assign(config.rethinkdb, {  
    db: 'timeline'
});
const app = express();  
const server = http.Server(app);  
const io = socketIO(server);

// Connecting to RethinkDB server
r.connect(db)  
    .then(conn => {
        // Index route which renders the index.html
        app.get('/', (req, res) => {
            fs.readFile(`${__dirname}/index.html`, (err, html) => {
                res.end(html || err);
            });
        });

        // The changefeed is provided by change() function
        // which emits broadcast of new messages for all clients
        r.table('messages')
            .changes()
            .run(conn)
            .then(cursor => {
                cursor.each((err, data) => {
                    const message = data.new_val;
                    io.sockets.emit('/messages', message);
                });
            });

        // Listing all messages when new user connects into socket.io
        io.on('connection', (client) => {
            r.table('messages')
                .run(conn)
                .then(cursor => {
                    cursor.each((err, message) => {
                        io.sockets.emit('/messages', message);
                    });
                });
            // Listening the event from client and insert new messages
            client.on('/messages', (body) => {
                const {
                    name, message
                } = body;
                const data = {
                    name, message, date: new Date()
                };
                r.table('messages').insert(data).run(conn);
            });
        });

        server.listen(3000, () => console.log('Timeline Server!'));
    })
    .error(err => {
        console.log('Can\'t connect to RethinkDB');
        throw err;
    });

There are some important details you must know when you work with RethinkDB, first almost all functions from this module works using callbacks or using Promises, if you choose Promises you can write well structure async functions with better error handlers.

The changefeed (via r.table('messages').changes()) feature is database’s subscriber, which is a query observer and returns any modification from a table, the combination with the io.sockets.emit() allows the server send realtime data for the clients.

Now, let’s create a simple migration script to prepare the database before starts the server. This migration is very common in relational databases. Create the database.js file with the script below:

const r = require('rethinkdb');  
const config = require('./config.json');  
let conn;

r.connect(config.rethinkdb)  
    .then(connection => {
        console.log('Connecting RethinkDB...');
        conn = connection;
        return r.dbCreate('timeline').run(conn);
    })
    .then(() => {
        console.log('Database "timeline" created!');
        return r.db('timeline').tableCreate('messages').run(conn);
    })
    .then(() => console.log('Table "messages" created!'))
    .error(err => console.log(err))
    .finally(() => process.exit(0));

And don’t forget to create the config.json which contains data to connect in the RethinkDB server:

{
    "rethinkdb": {
        "host": "localhost",
        "port": 28015
    }
}

To finish our application, now we must create the index.html which will be the client-side part for the users send messages in the timeline.

<!DOCTYPE html>  
<html>

<head>  
    <meta charset="utf-8">
    <title>Timeline</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <script src="/socket.io/socket.io.js"></script>
</head>

<body>  
    <form style="text-align:center;margin:50px 0">
        <label for="name">Name:</label>
        <input type="text" id="name" />
        <label for="message">Message:</label>
        <input type="text" id="message" />
        <button type="submit">Send</button>
    </form>
    <fieldset style="padding: 20px;width:50%;margin:0 auto">
        <legend style="text-align:center">Timeline</legend>
        <p id="messages"></p>
    </fieldset>
    <script>
        (function() {
            var socket = io();
            var form = document.querySelector('form');
            form.addEventListener('submit', function(e) {
                e.preventDefault();
                var name = e.target.querySelector('#name');
                var message = e.target.querySelector('#message');
                var data = {
                    name: name.value,
                    message: message.value
                };
                socket.emit('/messages', data);
                e.target.reset();
            });
            socket.on('/messages', function(data) {
                var messages = document.querySelector('#messages');
                var message = '<b>' + data.name + ':</b> ' +
                                data.message + '<br />';
                messages.innerHTML += message;
            });
        })();
    </script>
</body>

</html>  

Now we are ready to start this application! But before start the server you must run in the first time, the database’s migration to create the database and table for this project, so you just need to run this command:

node database.js  

If everything goes fine, You can start the server by running:

node index.js  

And you can play sending messages in this application accessing the http://localhost:3000 address.

Conclusion

The RethinkDB is awesome NoSQL! This database can provide a fully support for realtime applications just using changefeed + socket.io, in this link you can read more about what you can do using changefeeds.

Almost all functions can run using Promises which makes you write better code and you can use easily the ES7 async/await feature to simplify the promises functions too.

Protect Your Node.js App