Tag Archives: Node.js - Page 2

Very simple REST JSON node.js server

I want to build a modern web application (perhaps using AngularJS) or some mobile application, and I need to have some working server side to get started. There are of course plenty of options; .NET WebApi, LAMP, MongoDB, NodeJS + Express, and many more. But I want it stupid simple. This is tested on Linux, but everything should apply in Windows too.

I wrote a very simple REST/JSON server for node.js, and this is about it (source code in the end).

How to run it
Presuming you have nodejs installed:

$ node simple-rest-server.js

It now listens to port 11337 on 127.0.0.1 (that is hard coded in the code).

Configure with Apache
The problem with port 11337 is that if you build a web application you will get cross site problems if the service runs on a different port than the html files. If you are running apache, you can:

# a2enmod proxy
# a2enmod proxy_http

Add to /etc/apache/sites-enabled/{your default site, or other site}
ProxyPass /nodejs http://localhost:11337
ProxyPassReverse /nodejs http://localhost:11337

# service apache2 restart

You can do this with nginx too, and probably also with IIS.

Use from command line
Assuming you have a json data file (data99.json) you can write to (POST), read from (GET) and delete from (DELETE) the server:

$ curl --data @data99.json http://localhost/nodejs/99
$ curl http://localhost/nodejs/99
$ curl -X DELETE http://localhost/nodejs/99

If you did not configure apache as proxy as suggested above, you need to us the :port instead of /nodejs. In this case 99 is the document id (a positive number). You can add any number of documents with whatever ids you like (as long as they are positive numbers, and as long as the server does not run out of memory). There is no list function in this very simple server (although it would be very easy to add).

Using from AngularJS
The command line is not so much fun, but AngularJS is. If you create your controller with $http the following works:

function myController($scope, $http) {

  // write an object named x with id
  h = $http.post('http://localhost/nodejs/' + id, x)
  h.error(function(r) {
    // your error handling (may use r.error to get error message)
  })
  h.success(function(r)) {
    // your success handling
  })

  // read object with id to variable x
  h = $http.get('http://localhost/nodejs/' + id)
  h.error(function(r) {
    // your error handling
  })
  h.success(function(r) {
    x = r.data
  })

  // delete object with id 
  h = $http['delete']('http://localhost/nodejs/' + id)
  h.error(function(r) {
    // your error handling
  })
  h.success(function(r)) {
    // your success handling
  })
}

I found that Internet Explorer can have problems with $http.delete, thus $http[‘delete’] (very pretty).

What the server also does
The server handles GET, POST and DELETE. It validates and error handles its input (correctly, I think). It stores the data to a file, so you can stop/start the server without losing information.

What the server does not do
In case you want to go from prototyping to production, or you want more features, it is rather simple to:

  1. add function to list objects
  2. add different types of objects
  3. let the server also serve files such as .html and .js files
  4. use MongoDB as backend
  5. add security and authentication

The code
The entire code follows (feel free to modify and use for your own purpose):

/*
 * A very simple JSON/REST server
 *
 * http://host:port/{id}       id is a positive number
 *
 * POST   - create/overwrite   $ curl --data @file.json http...
 * GET    - load               $ curl http...
 * DELETE - delete             $ curl -X DELETE http...
 *
 */
glHost    = { ip:'127.0.0.1', port:'11337' }
glHttp    = require('http')
glUrl     = require('url')
glFs      = require('fs')
glServer  = null
glStorage = null

/* Standard request handler - read all posted data before proceeding */
function requestHandler(req, res) {
  var pd = ""
  req.on("data", function(chunk) {
    pd += chunk
  })
  req.on("end", function() {
    requestHandlerWithData(req, res, pd)
  })
}

/* Custom request handler - posted data in a string */
function requestHandlerWithData(req, res, postdata) {
  var in_url  = glUrl.parse(req.url, true)
  var id      = in_url["pathname"].substring(1) //substring removes leading /
  var retcode = 200
  var retdata = null
  var error   = null

  if ( ! /^[1-9][0-9]*$/.test(id) ) {
    error   = "Invalid id=" + id
    retcode = 400
  }

  if ( ! error ) switch ( req.method ) {
  case "GET":
    if ( ! glStorage[id] ) {
      error = "No object stored with id=" + id
      retcode = 404
    } else {
      retdata = glStorage[id]
    }
    break; 
  case "POST":
    try {
      glStorage[id] = JSON.parse(postdata)
      writeStorage()
    } catch(e) {
      error = "Posted data was not valid JSON"
      retcode = 400
    }
    break;
  case "DELETE":
    delete glStorage[id]
    writeStorage()
    break;
  default:
    error   = "Invalid request method=" + req.method
    retcode = 400
    break;
  }

  res.writeHead(retcode, {"Server": "nodejs"})
  res.writeHead(retcode, {"Content-Type": "text/javascript;charset=utf-8"})
  res.write(JSON.stringify( { error:error, data:retdata } ))
  res.end()

  console.log("" + req.method + " id=" + id + ", " + retcode +
    ( error ? ( " Error=" + error ) : " Success" ) )
}

function writeStorage() {
  glFs.writeFile("./db.json",JSON.stringify(glStorage),function(err) {
    if (err) {
      console.log("Failed to write to db.json" + err)
    } else {
      console.log("Data written to db.json")
    }
  })
}

glFs.readFile("db.json", function(err, data) {
  if (err) {
    console.log("Failed to read data from db.json, create new empty storage")
    glStorage = new Object()
  } else {
    glStorage = JSON.parse(data)
  }
})
glServer = glHttp.createServer(requestHandler)
glServer.listen(glHost.port, glHost.ip)
console.log("Listening to http://" + glHost.ip + ":" + glHost.port + "/{id}")

Build Node.js on Debian ARM

Update 2015-02-15: So far, I have failed building Nodejs v0.12.0 on ARMv5

I have a QNAP TS109 running Debian (port:armel, version:7), and of course I want to run node.js on it. I don’t think there are any binaries, so building from source is the way to go.

About my environment:

$ cat /etc/debian_version
7.2
$ gcc --version | head -n 1
gcc (Debian 4.6.3-14) 4.6.3
$ uname -a
Linux kvaser 3.2.0-4-orion5x #1 Debian 3.2.51-1 armv5tel GNU/Linux
$ cat /proc/cpuinfo
Processor       : Feroceon rev 0 (v5l)
BogoMIPS        : 331.77
Features        : swp half thumb fastmult edsp
CPU implementer : 0x41
CPU architecture: 5TEJ
CPU variant     : 0x0
CPU part        : 0x926
CPU revision    : 0

Hardware        : QNAP TS-109/TS-209
Revision        : 0000
Serial          : 0000000000000000

I downloaded the latest version of node.js: node-v0.10.25, and this is how I ended up compiling it (first writing build.sh, then executing it as root):

$ cat build.sh
#!/bin/sh
export CFLAGS='-march=armv5t'
export CXXFLAGS='-march=armv5t'
./configure
make install
$ sudo ./build.sh

That takes almost four hours.

A few notes…

make install
Naturally, make install has to be run as root. When I do that, everything is built again, from scratch. This is not what I expect of make install, and to me this seems like a bug. This is why I put the build lines into a little shell script, and ran the entire script with sudo. Compiling as root does not make sense

-march=armv4 and -march=armv4t
Compiling with -march=armv4t (or no -march at all, defaulting to armv4 I believe) results in an error:

../deps/v8/src/arm/macro-assembler-arm.cc:65:3: error:
#error "For thumb inter-working we require an architecture which supports blx"

You can workaround this by above line 65 in the above file:

#define CAN_USE_THUMB_INSTRUCTIONS 1

as I mentioned in my old article about building Node.js on Debian ARM.

-march=armv5te
I first tried building with -march=armv5te (since that seemed closest to armv5tel which is what uname tells me I have). The build completed, but the node binary generated Segmentation fault (however node -h did work, so the binary was not completely broken).

I do not know if this problem is caused by my CPU not being compatible with/capable of armv5te, or, if there is something about armv5te that is not compatible with the way Debian and its libraries are built.

Building Node.js on Debian ARM (old)

Update 20140130: I suggest you first have a look at my new article on the same topic.

I thought it was about time to extend my JavaScript curiosity to the server side and Node.js.

A first step was to install it on my web server, a QNAP TS-109 running Debian 6. I downloaded the latest version (v0.10.15), and did the usual:

$ ./configure
$ make

after hours:

../deps/v8/src/arm/macro-assembler-arm.cc:65:3: error: #error "For thumb inter-working we require an architecture which supports blx"

That is not the first time my TS 109 has been too old. However, the english translation of the above message is that you have to have an ARM cpu V5 or later, and it has to have a ‘t’ in its name (at least, this is what the source tells, see below). In my case

$ uname -a
Linux kvaser 2.6.32-5-orion5x #1 Sat May 11 02:12:29 UTC 2013 armv5tel GNU/Linux

so I should be fine. I found a workaround from which I extracted the essential part.

// We always generate arm code, never thumb code, even if V8 is compiled to
// thumb, so we require inter-working support
#if defined(__thumb__) && !defined(USE_THUMB_INTERWORK)
#error "flag -mthumb-interwork missing"
#endif

// ADD THESE THREE LINES TO macro-assembler-arm.cc

#if !defined(CAN_USE_THUMB_INSTRUCTIONS)
# define CAN_USE_THUMB_INSTRUCTIONS 1
#endif

// We do not support thumb inter-working with an arm architecture not supporting
// the blx instruction (below v5t).  If you know what CPU you are compiling for
// you can use -march=armv7 or similar.
#if defined(USE_THUMB_INTERWORK) && !defined(CAN_USE_THUMB_INSTRUCTIONS)
# error "For thumb inter-working we require an architecture which supports blx"
#endif

After adding the three lines, I just ran make again, and after a few hours more everything was fine. Next time I will try the -march or -mcpu option instead.