How to handle POST request in node.js

Solution 1:

I'm going to use the code you provided, and provide an answer more thorough than what's covered in your question to accommodate people in The Distant Future. I will also provide an answer that uses "Vanilla JS" (http://www.vanilla-js.com/) because I think too many hipsters say "use a framework" when you're trying to learn how this works. I think the reason they do that is because someone told them to "use a framework" when they were learning how this works. Because they aren't hackers, they didn't care to try and understand the process, so very often many of them do not understand how to do this on their own, without a framework (hence the ubiquitous "use a framework"). You will become a better hacker by understanding what's going on under the hood and I hope this answer helps you in that regard.

Now that you're wanting to accept POST (form) data via the form you're outputting, it's necessary to provide a routing mechanism in your server. This means that you'll tell your server to give the form to people visiting your site, but then if the user submits a form, Node will route the POST data to a little processing function. I've provided the complete answer first and then dissected it further down, to accommodate people wanting to learn from the code.

var http = require('http');
var qs = require('querystring');
var formOutput = '<html><body>'
  + '<h1>XYZ Repository Commit Monitor</h1>'
  + '<form method="post" action="inbound" enctype="application/x-www-form-urlencoded"><fieldset>'
  + '<div><label for="UserName">User Name:</label><input type="text" id="UserName" name="UserName" /></div>'
  + '<div><label for="Repository">Repository:</label><input type="text" id="Repository" name="Repository" /></div>'
  + '<div><label for="Branch">Branch:</label><input type="text" id="Branch" name="Branch" value="master" /></div>'
  + '<div><input id="ListCommits" type="submit" value="List Commits" /></div></fieldset></form></body></html>';
var serverPort = 8124;
http.createServer(function (request, response) {
  if(request.method === "GET") {
    if (request.url === "/favicon.ico") {
      response.writeHead(404, {'Content-Type': 'text/html'});
      response.write('<!doctype html><html><head><title>404</title></head><body>404: Resource Not Found</body></html>');
      response.end();
    } else {
      response.writeHead(200, {'Content-Type': 'text/html'});
      response.end(formOutput);
    }
  } else if(request.method === "POST") {
    if (request.url === "/inbound") {
      var requestBody = '';
      request.on('data', function(data) {
        requestBody += data;
        if(requestBody.length > 1e7) {
          response.writeHead(413, 'Request Entity Too Large', {'Content-Type': 'text/html'});
          response.end('<!doctype html><html><head><title>413</title></head><body>413: Request Entity Too Large</body></html>');
        }
      });
      request.on('end', function() {
        var formData = qs.parse(requestBody);
        response.writeHead(200, {'Content-Type': 'text/html'});
        response.write('<!doctype html><html><head><title>response</title></head><body>');
        response.write('Thanks for the data!<br />User Name: '+formData.UserName);
        response.write('<br />Repository Name: '+formData.Repository);
        response.write('<br />Branch: '+formData.Branch);
        response.end('</body></html>');
      });
    } else {
      response.writeHead(404, 'Resource Not Found', {'Content-Type': 'text/html'});
      response.end('<!doctype html><html><head><title>404</title></head><body>404: Resource Not Found</body></html>');
    }
  } else {
    response.writeHead(405, 'Method Not Supported', {'Content-Type': 'text/html'});
    return response.end('<!doctype html><html><head><title>405</title></head><body>405: Method Not Supported</body></html>');
  }
}).listen(serverPort);
console.log('Server running at localhost:'+serverPort);

And now for the breakdown explaining why I have done the things I did.

var http = require('http');
var qs = require('querystring');

First, you're going to add Node's built-in 'querystring' module to parse the actual form data.

var formOutput = '<html><body>'
  + '<h1>XYZ Repository Commit Monitor</h1>'
  + '<form method="post" action="/inbound" enctype="application/x-www-form-urlencoded"><fieldset>'
  + '<div><label for="UserName">User Name:</label><input type="text" id="UserName" name="UserName" /></div>'
  + '<div><label for="Repository">Repository:</label><input type="text" id="Repository" name="Repository" /></div>'
  + '<div><label for="Branch">Branch:</label><input type="text" id="Branch" name="Branch" value="master" /></div>'
  + '<div><input id="ListCommits" type="submit" value="List Commits" /></div></fieldset></form></body></html>';
var serverPort = 8124;

I've moved the form output up above our server/routing/form-handling mechanism, because the logic is then much easier to read. I also moved the server listening port information up here, because you then only have to change it in one place instead of many below.

http.createServer(function (request, response) {

(I usually shorten the parameters of this function to "req" and "res", but that's just my preference.)

  if(request.method === "GET") {
    if (request.url === "/favicon.ico") {
      response.writeHead(404, {'Content-Type': 'text/html'});
      response.write(notFound);
      response.end();

Here I've included a simple routing example. In this case, we're having our server listen for requests for "favicon.ico" -- a request made alongside almost all initial requests for a webpage by all the major browsers. This file is the little icon you can see up in the tabs of each webpage you're visiting. For our purposes, we don't need to serve a favicon, but we will handle inbound requests for it to show some basic routing mechanics.

    } else {
      response.writeHead(200, {'Content-Type': 'text/html'});
      response.end(formOutput);
    }

If your visitors point their browser to ANY other resource on your server with the default GET method (besides the "favicon.ico" we just handled above), we will serve them the form.

  } else if(request.method === "POST") {

Otherwise, if your visitors are pointing a POST at your server, it's very likely they have submitted the form they retrieved with the previous GET request.

    if (request.url === "/inbound") {

Here we are listening for inbound requests called "/inbound" which -- if you caught the little detail above -- is the "action" of our HTML form. As you may know, the "action" of the form tells the browser where to send the form data.

      var requestBody = '';
      request.on('data', function(data) {
        requestBody += data;
        if(requestBody.length > 1e7) {
          response.writeHead(413, 'Request Entity Too Large', {'Content-Type': 'text/html'});
          response.end('<!doctype html><html><head><title>413</title></head><body>413: Request Entity Too Large</body></html>');
        }
      });
      request.on('end', function() {
        var formData = qs.parse(requestBody);

This might look a little confusing, but I promise it's not. POST requests can be sent as multi-part messages from the client browser. With something as small as a few variables in a form, you won't likely ever see this, but as you scale the amount of data you handle, you will see this. If you're observant, you'll also see the if() statement asking about the length of the POST data. A malicious person can kill your server by uploading an endless file, but not if we take action. This limits the POST data body to about ten megabytes, but you should adjust accordingly. Knowing about these things prevents a future headache, and I don't want you to have a headache.

        response.writeHead(200, {'Content-Type': 'text/html'});
        response.write('<!doctype html><html><head><title>response</title></head><body>');
        response.write('Thanks for the data!<br />User Name: '+formData.UserName);
        response.write('<br />Repository Name: '+formData.Repository);
        response.write('<br />Branch: '+formData.Branch);
        response.end('</body></html>');
      });

And here is where we use the form data. Because of the nature of Javascript, these variable names are CASE SENSITIVE (such as "UserName" instead of "username"). Of course, you can do anything you want with this data (keeping in mind Node's event loop and asynchronous nature).

    }
    response.writeHead(404, 'Resource Not Found', {'Content-Type': 'text/html'});
    return response.end('<!doctype html><html><head><title>404</title></head><body>413: Request Entity Too Large</body></html>');

To continue our routing example, what we've done here is included a catch-all underneath the if() statement which sends the client a generic 404 "Not Found" reply to any POST request we haven't already handled.

  } else {
    response.writeHead(405, 'Method Not Supported', {'Content-Type': 'text/html'});
    return response.end('<!doctype html><html><head><title>405</title></head><body>405: Method Not Supported</body></html>');
  }
}).listen(serverPort);
console.log('Server running at localhost:'+serverPort);

And now we've just finished the code off, including a little bit of code to handle requests with strange methods. There are a few things I haven't addressed (function structure, empty form data, etc.), but there are indeed many ways to accomplish your goals. As one of my CS professors once said many years ago, there are so many ways to program a program that it's easy to see who's cheating by sharing their homework.

I hope that you (and anyone else) can see that it's not some esoteric or even slightly difficult process to do things in Node using its built-in modules instead of relying on external third-party libraries such as Express. Those libraries have their place in the world, but don't follow the herd: Make an informed decision about your code, because at the end of the day, you're the one responsible for it (not some people on Stack Overflow).