How to build real-time applications using Node.js and RethinkDB - Jscrambler Blog

August 04, 2016 0 Comments node, rethinkdb

How to build real-time applications using Node.js and RethinkDB - Jscrambler Blog


About RethinkDB

If you need a NoSQL database which works 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, where 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 in MongoDB, CouchDB or PostgreSQL.

There is some cool stuff 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 realtime searching;
  • Index support (simple, compound and multi);
  • Native web admin dashboard;

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 code below. To install this database I recommend you to read and follow the instruction of this link according to your operation system.

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

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

Now let's get to 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); // 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.connect(db) .then(conn => 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.connect(db) .then(conn => 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.connect(db).then(conn => r.table('messages').insert(data).run(conn)); }); }); server.listen(3000, () => console.log('Timeline Server!')); 

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 structured async functions with better error handlers. Another important thing is that RethinkDB is not thread-safe and all operations will work better if you connect, operate and close the connection, and that's why each query in this example is using the r.connect(db) before running any kind of database operation. So remember! RethinkDB works better running an operation per connection.

The changefeed feature is a database's subscriber, which is a query observer that returns any modification from a table, the combination with the io.sockets.emit() allows the server to send real time data for the clients.

Now, let's create a simple migration script to prepare the database before starting 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 to 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 starting the server you must first run 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 well, you can start the server by running:

node index.js 

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

Conclusion

RethinkDB is awesome NoSQL! This database can provide full 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 allows you to write better code and you can easily use the ES7 async/await feature to simplify the promises functions too.

How to build real-time applications using Node.js and RethinkDB - Jscrambler Blog


Tag cloud