Loading...

Jammie Mountz

Software Engineer

Your Server Cookbook – the basic ingredients

If you’ve been working mostly client-side, the server can look like this mysterious box that gives you the data you need to run your app. Unfortunately, the mysteriousness is a drawback the second something breaks and you don’t know even where to start looking. This blog post is a high-level cookbook for the general components in a server and how they fit together. I’m writing specifically a node server using the http module. Look into the express module if you’re actually writing your own server – I left that module out for the sake of transparency with server functions.

You and Your Server – an Introduction

In your app, you probably have AJAX calls to the server and get data back from it. The calls can either POST data to the server, or GET data from the server. These actions are triggered by the user. In a REST-ful app, the user action of landing on a page will make a GET call for that page’s content, and a POST action will route the data according to the page the user submitted the data from.

The client side ajax call

 

$.ajax({

url: your.server,

type: ‘POST’,

data: JSON.stringify(data),

contentType: ‘application/json’,

success: function (data) {

console.log(‘Data posted to server’);

},

error: function (data) {

console.error(‘Data fail’);

}

});

 

What this sends to the server

The AJAX call reaches out to your server and passes it data in the form of a request. The request tells the server from what page the post is being made, the data involved, and some metadata about the data.

The server itself is just a giant request handler for these processes. You can see that happening here:

 

var port = 8080;
var ip = “127.0.0.1”;
var server = http.createServer(handleRequest);
console.log(“Listening on http://” + ip + “:” + port);
server.listen(port, ip);

 

The server is created with the http module and then passed “handleRequest,” which is a function we define. The data enters the server and is immediately passed to that function. That function needs to take the request, handle it, and generate a response.

These 5 general things need to happen in the server code:

ON THE REQUEST…
1. Determine if the request is a POST or a GET
2. If its a GET, it needs to find and serve the data needed.
3. If it’s a POST, it needs to take the data from the request.

ON THE RESPONSE…
4. A head needs to be written for the response, which includes the headers and status
5. The response needs to be closed.

You can abstract our handler out to the following function:

 

handleRequest = function (request, response) {

var action = actions[request.method];

if ( action ) {

action(request, response);

} else {

sendResponse(response, “Not Found”, 404);

}

};

 

The function, which is passed into the http createServer method when the server is defined, takes the request and response object as arguments. The variable “action” looks at the request.method, which returns “POST” or “GET”, and then looks at our actions object (not yet created) to lookup the function on how to handle it. We call it if it’s defined, or we send an error with “sendResponse” (also not yet created). We’ve taken care of #1 on our list of five!

Let’s flesh out the actions object first, beginning with our GET.

And we’re pulling in the path module as the path variable – not shown here, because it’s a require statement at the top.

 

var actions = {

GET: function(request, response) {

var parts = path.parse(request.url);

var urlPath = parts.pathname === ‘/’ ? ‘/index.html’

: parts.pathname;

serveAssets(response, urlPath, function(){

if (found) {

sendResponse(response, data, 404);

}

}

}

Here, we’re assigning a function to the GET key that takes the request and response. We’re getting these from when this function is called in our handleRequest, which is getting them from passing this function into the http generation of the server. We’re using path to parse the url the GET is coming from, which is a property of request. The next line just checks if we should serve the index or not – if not, we’re going to keep our pathname to be where ever the user generated the GET.

The next thing we need is to get the file that has the page we need to serve, based on the URL for the GET. Pretend we’ve wrote a function called “serveAssets” that uses the fs module to read our file that has our HTML. The method fs.readFile returns the file as a data object, which we then pass into our response after writing our head with our headers and status code. Note that we pass the data into response.end, which sends the response to our client along with the data needed to generate that page.

An interesting thing to note here is the use of callbacks and the need for asynchronous programming when writing serveAssets. The file will take a certain amount of time to read, and the response must be returned when fs is done reading, or it will be undefined. You can use callbacks or promises (the Q and bluebird modules are highly recommended) to make sure your data isn’t undefined.

We’ve satisfied number 2 on our list! Lets move on to our POST method in our actions object.

 

var actions = {

POST: function(request, response) {

data = “”;

request.on(“data”, function(chunk){

chunk+= data

});

request.on(“end”, function(){

callback(data);

sendResponse(response);

})

}

}

 

We pass in our request and response, and then use the request.on method to grab our data from the request. This is an asynchronous process, which means we use callbacks to make sure our data is completely downloaded before we move on. As the data comes through in chunks, aptly named “chunks,” we add it to our data string. Once all the chunks have come through, the end event is triggered. We can run any callback on our data, processing it as needed, and then we send a response back to the browser.

That satisfies number 3! Now lets look at 4 and 5 together – we use the sendResponse function in our POST and GET methods, and in our handleRequest function if the action is not defined. But what’s happening in that function that writes the head and closes the response? Lets write that function now.

We’re walking into this with our headers already available in an object –

 

headers = {

“access-control-allow-origin”: “*”,

“access-control-allow-methods”: “GET, POST,

PUT, DELETE, OPTIONS”,

“access-control-allow-headers”: “content-type, accept”,

“access-control-max-age”: 10, // Seconds.

‘Content-Type’: “text/html”

};

 

 

sendResponse = function(res, obj, status) {

status = status || 200;

response.writeHead(status, headers);

response.end(obj);

}

 

This response sender determines the status based on if one was passed in, writes the head using that and our headers object, and then ends the response. Perfecto! Numbers 4 and 5 are satisfied.

And thats a server for you.

tl;dr – Servers process requests and generate responses. The requests can be a POST or a GET, and the response can serve the data or just close the interaction. That’s all!

Fluent in Spanish and JavaScript, salsa dancer, lover of material design.

Living in San Francisco, CA