Setup hosted RethinkDB

Use RethinkDB hosted on Compose.io as a REST api data end point.

I have played with a local RethinkDB installation. Now I wanted to see how simple / difficult is to get a hosted instance. I signed up for a free trial at compose.io, a DB as a service provider. The initial setup took 60 seconds, and the cluster with two servers was ready. In order to connect and see the web admin interface I had to

  • Create new user and copy my public SSH key
  • Run ssh tunnel with port forwarding. The default mapping from remote port to 127.0.0.2:8080 address did not work, so I mapped to localhost:8082 port instead. This worked and I could open localhost:8082 in the browser and see the hosted database interface, just as if it were locally run instance.
web ui, not real ip addresses
1
2
3
ssh -N [email protected] -p 10000 \
-L localhost:8082:10.11.12.2:8080 \
-L localhost:8083:10.11.12.3:8080

Similarly, I mapped ports for a driver to connect to the remote DB via local port. The default RethinkDB driver connection port is 28015 and we can map it to our local port 27000 for example.

driver, not real ip addresses
1
2
3
ssh -N [email protected] -p 10000 \
-L localhost:27000:10.11.12.2:28015 \
-L localhost:27001:10.11.12.3:28015

Trying remote DB

The remote database is accessible via localhost and mapped port. All I needed to do was to provide these parameters as arguments to the thinky.

1
2
3
4
5
var options = {
host: 'localhost',
port: 27000
};
var thinky = require('thinky')(options);

The rest of the script can be in my Try RethinkDB blog post. The remote server has received the data and I could see it via the admin web interface.

Access DB from an API server

We should not access the database directly from a web server, instead I prefer wrapping the data in a dedicated REST server. To do so I created a simple application on Heroku that uses Express server to parse input parameters and output JSON.

Access DB from Heroku app

To allow an application hosted on Heroku to connect to the RethinkDB I had to add ssh tunnel command to the [Procfile][https://devcenter.heroku.com/articles/procfile]. This command runs when the dyno starts and makes the remote database appear as a local one at the specific address (like localhost:28015).

Procfile, not real ips
1
2
web: /bin/bash -c '(nohup ssh -n -i compose-heroku-key -o StrictHostKeyChecking=no \
-N [email protected] -p 1000 -L 127.0.0.1:28015:10.11.12.2:28015 &); node index.js'

After adding a tunnel, the Procfile tells the Heroku boot process to run node index.js which is our Express server.

Return data on GET

First, I configured an Express server using Getting Started with Bookshelf.js as an example. The express server configuration deals with JSON only

express setup
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var express = require('express');
var app = express();
var logger = require('morgan');
var bodyParser = require('body-parser');

var allowCrossDomain = function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
next();
};

app.use(logger('dev'));
app.use(allowCrossDomain);
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true }));
// parse application/json
app.use(bodyParser.json());
// parse application/vnd.api+json as json
app.use(bodyParser.json({type: 'application/vnd.api+json'}));

// RethinkDB will go here

var port = process.env.PORT || 3000;
app.listen(port, function() {
console.log('Express started at port', port);
});

The RethinkDB-specific setup only had Thinky stuff

Data connection
1
2
3
4
5
6
7
8
9
10
var options = {
host: 'localhost',
port: 28015 // based on ssh tunnel
};
var thinky = require('thinky')(options);
var Show = thinky.createModel('tv_shows', {
id: String,
name: String,
episodes: Number
});

Then I added first route to return all tv shows

1
2
3
4
5
6
7
8
9
app.get('/api/tv_shows', function(req, res) {
return Show.getJoin()
.run()
.then(res.send.bind(res))
.catch(function(error) {
console.log(error);
res.send('An error occured');
});
});

and another route to return specific tv show JSON given an id

1
2
3
4
5
6
7
8
9
app.get('/api/tv_shows/:id', function(req, res) {
return Show.get(req.params.id)
.getJoin()
.then(res.send.bind(res))
.catch(function(error) {
console.log(error);
res.send('An error occured');
});
});

This setup allows only getting the data, not adding new records or editing yet.

Extras