Implementing Node Server HTTP POST, GET vs POST

In my previous step towards implementing a handy minimal WebGL viewer, I started equipping it with a REST API.

Now I'll expand that just a little bit by adding support for POST requests as well:

HTTP GET and POST Limitations

So far, my miniscule REST API just implements a placeholder skeleton returning v1: Hello World! to a HTTP GET request to the URL api/v1.

Before starting with the REST API approach and the 3D WebGL stuff, I implemented a little 2D SVG viewer as a Heroku-hosted Node.js server using a query string appended to the URL to pass in data to the server.

This method is limited by the maximum practical length of a URL. The limitation to 256 characters is a myth, cf. the various discussions on limit to the length of a GET request, limits on query string, GET and URL parameters.

Still, even if a couple of thousand characters can be used, it will still be too limiting to allow passing around any serious amount of geometrical data.

Passing geometry data with potentially hundreds of face definitions requiring thousands of vertex and normal coordinates will require more than a few thousand bytes.

A POST request, however, can handle several MB.

So I started digging into the implementation of a POST request handler for my node server.

HTTP GET Implementation Starting Point

To be clear about where we are starting from, here is the HTTP GET implementation that I ended up with last time; it defines how to handle the api/v1 route in server.js like this:

var api = require('./routes/api');
app.get('/api/v1', api.v1);

The implementation module routes/api.js defines the method v1 as:

exports.v1 = function(req, res) {
  res.send('v1: Hello World!');
};

For the entire starting point implementation code, look at NodeWebGL release 1.0.0.2.

Struggles Implementing the POST Handler

The exploration how to add a POST handler took longer than I would have expected due to the evolution of the packages being used.

I started off from this thread on how to extract POST data in node.js, plus the Hack Sparrow tutorials on Express.js, Handling Forms – POST, GET Requests, saying "handling POST data is pretty easy – just look for it in the req.body object" and on form handling and processing in Express.js, followed by the more complete description on creating a simple RESTful web app with node.js, express, and mongodb.

Trying to use bodyParser as described there produces this error on my system, though:

$ node server.js
Error: Most middleware (like bodyParser) is no longer
bundled with Express and must be installed separately.
Please see https://github.com/senchalabs/connect#middleware.

So I took a look at the Connect project middleware, struggled a bit more and finally hit upon this workable and very helpful example reading form input with Node.js, Express 4.0 and body-parser, still making use of the body-parser Node.js body parsing middleware:

var bodyParser = require('body-parser');

However, the body-parser usage has evolved and needs to be modified.

I cannot simply use it as presented in that example without being warned off from deprecated usage:

// use the deprecated bodyParser middleware for all routes
app.use( bodyParser() );

This generates the following warnings:

The individual middleware packages provided by body-parser are json, raw, text and urlencoded.

I am testing my initial implementation with a form. By trial and error, I determined that I need the urlencoded version to handle the form input fields.

So I solved the two warnings by switching to this:

app.use( bodyParser.urlencoded() );

That generates a new warning, though, complaining about the lack of extended options:

Adding the options finally enables me to handle my form POST request with no warnings at all:

app.use( bodyParser.urlencoded({ extended: true }) );

Complete GET and POST Implementation

The entire node.js server basically still just consists of two modules, server.js and routes/api.js.

Here is the mainline implementation in the former:

var express = require('express');
var app = express();

app.set('port', (process.env.PORT || 5000));

app.use(express.static(__dirname + '/public'));

app.get('/', function(req, res) {}); // leads to public/index.html

var bodyParser = require('body-parser');
app.use( bodyParser.urlencoded({ extended: true }) );

var api = require('./routes/api');
app.get('/api/v1', api.v1);
app.post('/api/v1', api.v1);

app.listen(app.get('port'), function() {
  console.log("Node WebGL app is running at localhost:"
    + app.get('port'));
});

As you can see, calling the server with no route appended is handled and leads to the static index.html provided in the public folder.

The REST API is supported by the route api/v1 and responds to both GET and POST requests.

They are implemented by routes/api.js like this:

exports.v1 = function(req, res) {
  if (req.method == 'GET') {
    res.send('API v1 GET: Hello World!');
  }
  else if (req.method == 'POST') {
    res.send('API v1 POST: ' + JSON.stringify(req.body));
  }
};

GET is answered with a simple message – this could be used to provide API usage instructions, for instance – whereas POST replies by echoing the body data.

Interactive Testing Framework

Here is a HTML form for testing the first POST request.

It uses a JavaScript helper function to open a new window to host the server:

function open_node_server_window()
{
  var local = false;
  var base_url = local
    ? 'http://localhost:5000'
    : 'https://nameless-harbor-7576.herokuapp.com';
  var api_url = '/api/v1';
  var w = 400;
  var h = 400;
  var features = 'width=' + w.toString()
    + ',height=' + h.toString();

  window.open(base_url + api_url, 'node_server', features);
}

The open_node_server_window function is called from the submit button onclick event before the form routes the node server response to it through the form action and target attributes:

<form method="post"
  action="https://nameless-harbor-7576.herokuapp.com/api/v1"
  target="node_server">

  <p>Form submitting a HTTP POST request:</p>
  <table>
    <tr><td>position:</td><td><input type="text"
      name="position" value="1, 1, -1,  1, 1, 1,  1, -1, 1,..."></td></tr>
    <tr><td>normal:</td><td><input type="text"
      name="normal" value="1, 0, 0,   1, 0, 0,   1, 0, 0,..."></td></tr>
    <tr><td>indices:</td><td><input type="text"
      name="indices" value="0, 1, 2,  0, 2, 3,..."></td></tr>
    <tr><td></td><td><input type="submit"
      onclick="open_node_server_window()"></td></tr>
  </table>
</form>

Here it is for you to try out live:

Form submitting a HTTP POST request:

position:
normal:
indices:

As said, you can also call it with the HTTP GET verb, in which case the response is different:

Form submitting a HTTP GET request:

Finally, the original non-API-driven version still works just as before; I added the REST API while preserving upward compatibility and keeping the original server running continuously at the same time:

Download

The complete node server implementation is available from the NodeWebGL GitHub repo, and the version discussed here is release 1.0.0.3.

What Next?

Even with the placeholder GET and POST functionality in place, the REST API still does nothing useful whatsoever yet.

Starting to pass in useful data and doing something interesting with it is next on the agenda, of course.

Specifically, driving a WebGL viewer from the posted data, similar to the one currently defined in the static public/index.html file.

Stay tuned.