Jutting Bytes

Digressions of a research engineer

Node.js Makes You Strong... Within Minutes

| Comments

I’ve never been much of an “interpreted” languages guy. I’ve grown up with C, C++ and Objective-C, OpenGL, GTK, Qt and Cocoa, and have always fancied performance over simplicity, compilers and linkers over interpreters, even though I must admit that one can see a certain beauty in low level languages, I still remember that I use to read the Free BSD kernel source code on the bench of my university.

However, and especially, when it came for the web, I have always picked up the right languages and schemes: HTML, Javascript and CSS, RESTful APIs and MVC patterns. That gave me a medium experience in web development, but also introduced me to other interpreted languages such as Python for the Django framework, and most of all, Ruby for the RubyOnRails framework.

With such achievements as Electron, Atom and especially Node.js together with its huge package system, npm, I’ve come to reconsider Javascript as a real option for whatever development I want to start, especially when it involves a hybrid infrastructure (such as web services or so).

Unsurprisingly, I came to learn Javascript more in depth when using Qt’s QML, and reading the excellent book “Eloquent JavaScript”: http://eloquentjavascript.net.

Recently, I came to consider the refactoring of a significant scientific application on bio mechanical models that uses CORBA in a remote-address space configuration, essentially to make Remote Procedure Calls (RPC), for which CORBA is way too heavy and difficult to maintain. Here came the thought of JSON-RPC, which seemed like a lightweight, cross platform and simple alternative that fits the needs of this application infrastructure. That is to say that JSON-RPC calls are a design, a formalization, a protocol, that can be achieved with many languages, including C/C++ and Javascript, which is enough in my case.

So I gave Node.js a sceptical look, with its shy 37 classes (as of v6.3.1), I’ve counted them all ;–)

At first look, it reminded me of the C/C++ wrappers I used to generate for my own C++ frameworks using the excellent SWIG, but with an intent over the operating system primitives, with classes such as Buffer, Console, FileSystem, Path, Process, Readline, Stream, ZLIB and so on. So far so good.

Also, it has a serious concern on the web, with its HTTP, HTTPS or Net classes. The “Hello World” example is, by the way, a web server implemented in no more than a few lines. At this point, I remembered the burden of installing the right ruby version in order to get the right RubyOnRails version that fits the dependencies needed. As an example, thanks to rbenv, I have many ruby installations: one for my Octopress instance, another one, up to date, for my various packaging scripts. If Gemfile(s) exist, they do not prevent you from installing many versions of ruby.

Once the “Hello World” example run, after a brew install node (which gives you npm), I focused on my current problem, which was first: RPC calls, then: a web front end to display current calculations and results. So I dug up the plethora of packages available and choosed the one I preferred (for the mention, the one that inherits from the “native” http server implementation, as to know: jayson).

After 30 minutes of googling and reading, I had an RPC mechanism that worked, using two procedures, namely an addition and a subtraction, as a proof of concept for more complex implementations, comforted by the fact, that using Node.js, I have a Process class (as well as Cluster and Stream ones) that can make benefit of different binaries.

When starting a project using Node.js, it is recommanded to start with the following command:

1
$ npm init

This command prompts for some values and generate a file called packages.json that contains many information on your project but also its eventual dependencies. Here is mine.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
    "name": "odin-js",
    "version": "0.0.1",
    "description": "Poc at providing both JSONRPC clients and servers.",
    "main": "server/index.js",
    "scripts": {
        "start": "node server/index.js"
    },
    "author": "Julien Wintz",
    "license": "BSD-3-Clause",
    "dependencies": {
        "express": "latest",
         "jayson": "latest",
            "ejs": "latest"
    }
}

Many additional fields can exist, refer to the documentation for their description. What is relevent so far is the fact that my packages files sates that my project depends on express, jayson and ejs (which stands for embeddedjs). So the next step is to install these dependencies that will end up in a directory at the root of your project named node_modules (I’ve tried to change its name and location but it is explicitly stated in the documentation that it is strongly advised not to do so).

1
$ npm install

As this point, npm start will start to interpret server/index.js. As my full project hierarchy is as follows.

1
2
3
4
5
6
7
8
9
10
11
12
13
✓ jwintz@inextremis ~/Development/odin-js (master *) $ tree
.
├── README.md
├── client
│   └── index.js
├── package.json
└── server
    ├── 404.ejs
    ├── footer.ejs
    ├── header.ejs
    ├── index.ejs
    ├── index.js
    └── navigation.ejs

So let’s start at looking the client side. In its minimum state it consists in setting up jayson as to connect to a host, and to perform to minimal RPC calls, with a callback that prints out the result of the call.

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// Version: $Id$
//
//

// Commentary:
//
//

// Change Log:
//
//

// Code:

// /////////////////////////////////////////////////////////////////
// JSON-RPC requirements
// /////////////////////////////////////////////////////////////////

var jayson = require('jayson');

// /////////////////////////////////////////////////////////////////
// JSON-RPC client
// /////////////////////////////////////////////////////////////////

var client = jayson.client.http({
    port: 3001
});

// /////////////////////////////////////////////////////////////////
// JSON-RPC client calls
// /////////////////////////////////////////////////////////////////

client.request('add', [1], (error, response) => {
    console.log("JSONRPC:add -> " + response.result);
});

client.request('sub', [2], (error, response) => {
    console.log("JSONRPC:sub -> " + response.result);
});

Using the Readline class that comes with Node.js, we can quickly implement a very minimalistic console interface that prompts for operations to be remotly performed.

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// /////////////////////////////////////////////////////////////////
// JSON-RPC console
// /////////////////////////////////////////////////////////////////

const readline = require('readline');

const interface = readline.createInterface({
     input: process.stdin,
    output: process.stdout,
    prompt: '$ '
});

console.log('Hit CTRL-D to quit');

interface.on('line', (operation) => {

    if (operation === "add")
        client.request('add', [1], (error, response) => {
            console.log("-> " + response.result);
            interface.prompt();
        });

    else if (operation === "sub")
        client.request('sub', [2], (error, response) => {
            console.log("-> " + response.result);
            interface.prompt();
        });

    else
        interface.prompt();
});

interface.on('close', () => {
    interface.clearLine(process.stdout, -1);
});

interface.prompt();

//
// index.js ends here

So simple. Little is beautiful. Let’s have a look at the server side, which is divided in two parts: an http server running on the port 3000, and a JSON-RPC server running on the port 3001.

Let’s start with the JSON-RPC side which is the simplest one, and very intuitive.

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
26
27
28
29
30
31
32
33
34
35
36
37
// /////////////////////////////////////////////////////////////////
// JSON-RPC requirements
// /////////////////////////////////////////////////////////////////

var jayson = require('jayson');

// /////////////////////////////////////////////////////////////////
// JSON-RPC procedures
// /////////////////////////////////////////////////////////////////

var jayson_server = jayson.server({

    add: (arguments, callback) => {

        value += arguments[0];

        callback(null, value);
    },

    sub: (arguments, callback) => {

        value -= arguments[0];

        callback(null, value);
    }
});

// /////////////////////////////////////////////////////////////////
// JSON-RPC server startup
// /////////////////////////////////////////////////////////////////

jayson_server.http().listen(3001, () => {
    console.log('JSON-RPC server running on port 3001');
});

//
// index.js ends here

In the above snippet, we simply create a server that contains two operations, namely add and sub, that operate on the simplest model ever:

1
var value = 0;

The latest step is to implement a RESTful server which allows you to define routes and layouts to be rendered, just as RubyOnRails does, but in a minimal amount of code.

For this, I have chosen express.js and embedded.js.

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// Version: $Id$
//
//

// Commentary:
//
//

// Change Log:
//
//

// Code:

// /////////////////////////////////////////////////////////////////
// RESTful requirements
// /////////////////////////////////////////////////////////////////

var express = require('express');

// /////////////////////////////////////////////////////////////////
// RESTful server
// /////////////////////////////////////////////////////////////////

var express_server = express();
var express_router = express.Router();

// /////////////////////////////////////////////////////////////////
// RESTful router setup
// /////////////////////////////////////////////////////////////////

express_router.use((request, result, next) => {
    console.log(request.method + " - " + request.path);
    next();
});

express_router.get("/", (request, result) => {
    result.render("index", {"value" : value});
});

// /////////////////////////////////////////////////////////////////
// RESTful server setup
// /////////////////////////////////////////////////////////////////

express_server.set('view engine', 'ejs');
express_server.set('views', __dirname);

express_server.use("/", express_router);

express_server.use("*", (request, result) => {
    result.render("404");
});

// /////////////////////////////////////////////////////////////////
// RESTful server startup
// /////////////////////////////////////////////////////////////////

express_server.listen(3000, () => {
    console.log(' RESTful server running on port 3000');
});

The code is self-explanatory. We setup a router that gets “/” to serve the index.html page (that is the expressjs part), using “ejs” as an view engine (that is the embeddedjs part), and everything else as a 404.html page.

Using embeddedjs, we have an acces to our model, so we will be able to display the current value after our remote procedure calls, that is to say, that within our index.ejs file, we can inject javascript values.

1
2
3
4
5
6
7
8
9
10
11
12
<!doctype html>

<html lang="en">
  <% include header %>
  <body>
    <% include navigation %>
    <div class="container">
      <p>Current value: <%= value %></p>
    </div>
  </body>
  <% include footer %>
</html>

This is a simple as that! Note the use of the <% include ... %> directives that allows for partials, other parts of the html document that are repeated for multiple files (namely index and 404 in our case).

For the sake of completeness, here is the header.ejs, where we retrieve bootstrap by the help of a Content Delivert Network (CDN), and the footer.ejs that finalizes the HTML page.

1
2
3
4
5
<head>
  <meta charset="UTF-8">
  <title>Odin</title>
  <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
1
2
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>

As a conclusion, I am very enthusiast, and very much impressed by the ease of use of Node.js and its packages. I look forward giving Electron a shot and blog about it a little more.


Comments