Nodejs Book: Chapter 6

In the previous chapter, we created a very simple proof of concept ajax request
from within a client Javascript. In this chapter we’re going to expand on this
example so that instead of just the static text, “Hello, World!” change the
request so we return “Hello !”. The output from this chapter will look
like the following.

The client side code is the most similar so we’ll take a look at that first.

File: post.html

<!DOCTYPE HTML>
<html>

    <head>

        <meta charset="utf-8"/>
        <title>Ajax Request</title>

    </head>

    <body>

        <input type="text" id="inputName"/>
        <button id="clickBtn">Click Me!</button>
        <p id="responseText"></p>

        <script type="text/javascript" src="js/post.js"></script>

    </body>

</html>

Above is the code for the hmtl, it’s almost entirely identical, with the only
difference being that we add a new input element, for the user to enter
their name. The code for the client side Javascript is largely unchanged as
well. The source code is as listed below.

File: js/post.js

"use strict";

var inputName = document.getElementById("inputName");
var clickBtn = document.getElementById("clickBtn");
var responseText = document.getElementById("responseText");

clickBtn.addEventListener("click", function (event) {

    var args = {
        "name" : inputName.value
    }

    var xml = new XMLHttpRequest();
    xml.open("POST", "/api/hello", true);
    xml.send(JSON.stringify(args));

    xml.onload = function() {

        responseText.textContent = xml.responseText;

    }


});

The main difference is that we now create a JSON object to be sent to the server,
which we refer to as args. We also set the request type as “POST” for the xml.open
function and input the string representation of the arguments we want sent to the
server.

If your wondering where the “GET” and “POST” types come from, they are two specific
types of http request. GET is likely the most common, as it only sends a URL
with a set of headers to the server to ask for a given resource. POST is different
in that in addition to a URL and headers, a body is also sent with the request. In
this case our JSON string is attached to the body of the request to the server.

There are also other standard http requests. HEAD, which just requests HTTP header
responses with no body. PUT to send information, DELETE to request the deletion
of a resource, OPTIONS which returns the HTTP methods the server supports, and CONNECT
which converts the connection to a TCP tunnel.

Which brings us to the source for the actual server:

File: callback.js

"use strict";

const http = require("http");
const handleFile = require("handle-file");

const server = http.createServer();
server.on("request", handleRequest);
server.listen(8080, handleListen);

function handleRequest(req, res) {

    if(req.method === "GET") {

        handleFile(req, res);

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

        switch(req.url) {
            case "/api/hello":

                api_say_hello(req, res);

            break;
            default:

                res.writeHead( 204, { "Content-Type" : "text/plain" });
                res.end( "Empty Response" );

            break;
        }

    } else {

        res.writeHead( 204, { "Content-Type" : "text/plain" });
        res.end( "Empty Response" );

    }

}

function handleListen( ) {

    console.log("Server is listening on port 8080");

}

function api_say_hello( req, res ) {

    var json_str, json;

    var json_str = "";

    req.on("data", function(data) {

        json_str += data;

    });

    req.on("end", function() {

        try {
            json = JSON.parse(json_str);
        } catch(err) {
            res.writeHead( 400, { "Content-Type" : "text/plain" });
            return res.end( "Invalid JSON parameter" );
        }

        res.writeHead(200, { "Content-Type" : "text/plain" });
        res.end("Hello " + json.name);

    });

}

The first thing that sticks out is the handle function request.

function handleRequest(req, res) {

    if(req.method === "GET") {

        handleFile(req, res);

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

        switch(req.url) {
            case "/api/hello":

                api_say_hello(req, res);

            break;
            default:

                res.writeHead( 204, { "Content-Type" : "text/plain" });
                res.end( "Empty Response" );

            break;
        }

    } else {

        res.writeHead( 204, { "Content-Type" : "text/plain" });
        res.end( "Empty Response" );

    }

}

We can use the request method to quickly filter what the intent of the request is.
For GET requests, we can assume a file is being requested and serve a file. In the
case of POST, we can consider it a call to the server from an ajax client, and filter
out which function should be called to handle the request. And lastly to make sure
that in the case of any other method type are handled and simply returned an empty
response.

function api_say_hello( req, res ) {

    var json_str, json;

    var json_str = "";

    req.on("data", function(data) {

        json_str += data;

    });

    req.on("end", function() {

        try {
            json = JSON.parse(json_str);
        } catch(err) {
            res.writeHead( 400, { "Content-Type" : "text/plain" });
            return res.end( "Invalid JSON parameter" );
        }

        res.writeHead(200, { "Content-Type" : "text/plain" });
        res.end("Hello " + json.name);

    });

}

The actual code for handling our “/api/hello” reply is written above. When a client
sends a POST request to a server, it sends the information in packets. And if
the information being sent to the server is large, the client server may have
to send the information in stages. So we listen for data to be sent and add it
to our JSON string, and then once the information has finished, we parse the JSON
string. Once that’s finished we are able to send our response.

So in this chapter we managed to send a simple JSON string to the server and got
a response. So what about uploading files? We’ll work on that in the next chapter.