Node Servers

August 20, 2017

In the process of learning the MEAN stack, I’ve come across several tutorials that use Node functions and libraries but never explain how they work. They may show a Node function and how to use it in a particular use case, but didn’t get into the details of how the underlying mechanisms worked. This way of learning has never really worked for me and I end up struggling to use what’s being taught outside the tutorial context. So I decided I needed to start from the ground up in Node specifically, learning how and why everything works.

De-Mystifying Node

I started the Understand Nodejs course from Udemy and so far it’s been exactly what I needed. The instructor is fantastic and really helps to understand the core functionality of Node and explains what’s actually happening under the hood. It has totally demystified many core concepts within Node for me and I am gaining an understanding of how to use the Node core libraries to build custom and tailored applications. I am about 2/3rds of the way through the course now and just finished the section dealing with creating servers.

Servers

This is such a core concept in Node that I thought I would go through my learning process and include code samples with detailed comments that really helped me understand what was going on. I’ve tried to be detailed in the comments to break down what’s happening at each step. We started with a very basic server that returned plain text in its response.

var http = require('http');
// Creates new Server object
// and takes a callback as param.
// The callback is an event listener;
// When server emits particular event,
// this function will be called.
// Callback takes 2 parameters,
// request (type IncomingRequest)
// and response (type ServerResponse).
// Returns a Server object
http.createServer(function(req, res) {
// Here we build our response to a request
// ServerResponse.writeHead takes as parameters:
// 1. statusCode: number
// 2. reasonPhrase?: string
// 3. headers?: OutgoingHttpHeaders
// writeHead writes header of response
res.writeHead(200, {'Content-Type': 'text/plain'});
// end() method sends content of the response to the client
// and signals to the server that the response
// has been sent completely.
// If you are going to send anything else, use
// write() instead of end().
// end() must be called on each response.
// end() takes params:
// 1. data: string
// 2. encoding?: string
// 3. callback?: Function
// If callback is specified, it wil be called when
// the response stream is finished.
res.end('Hello world\n');
}).listen(1337, '127.0.0.1');
// We call listen() on the Server object that is returned.
// listen() takes the following params:
// 1. port?: number
// 2. hostname?: string
// 3. backlog?: number
// 4. listeningListener?: Function
// listen() returns a Server object.
// 127.0.0.1 is standard internal IP address of localhost.
// Allows us to make request via localhost:1337
// Execute file in command line;
// It keeps running and listens for requests.
// Give it a request by navigating to localhost:1337 in browser.
// This looks on localhost for a program that
// is listening on port 1337 and gives it HTTP request
// that browser is going to make.
// Node code will be run because request event will be emitted
// and we have server listening and we gave it a function;
// function will be invoked, which will read the request
// and allow me to write the response.
// HTTP response will be built and sent back to the browser,
// and browser will go on and do whatever it wants with it
// (whatever it has been programmed to do with that
// particular type of response).
view raw server-plain.js hosted with ❤ by GitHub

We then moved on and created another simple server that returned HTML content in the response.

var http = require('http');
var fs = require('fs');
http.createServer(function(req, res) {
// Specify html content type
res.writeHead(200, { 'Content-Type': 'text/html' });
// Read contents of index.htm file synchronously,
// Saved as string in var html
var html = fs.readFileSync(__dirname + '/index.htm');
// Send contents of index.htm file as body of response
res.end(html);
}).listen(1337, '127.0.0.1');
view raw server-html.js hosted with ❤ by GitHub

<html>
<head></head>
<body>
<h1>Jelly Filled Donut</h1>
</body>
</html>
view raw index.htm hosted with ❤ by GitHub

Next we created an HTML file that we wanted to have dynamic content. We wanted to replace {Message} with with a message variable.

var http = require('http');
var fs = require('fs');
http.createServer(function(req, res) {
// Specify html content type
res.writeHead(200, { 'Content-Type': 'text/html' });
// Read contents of index.htm file synchronously,
// Saved as string in var html.
// Use 'utf8' param to specify encoding and ensure you are
// dealing with a string rather than a buffer.
var html = fs.readFileSync(__dirname + '/index2.htm', 'utf8');
var message = 'Bostom Creme Donut';
// Takes html string and replaces instances of
// '{Message}' with value of message var
html = html.replace('{Message}', message);
// Send contents of index.htm file as body of response
res.end(html);
}).listen(1337, '127.0.0.1');

<html>
<head></head>
<body>
<h1>{Message}</h1>
</body>
</html>
view raw index2.htm hosted with ❤ by GitHub

Finally, we went back to the original server returning HTML and took out the synchronous readFileSync operation and replaced it with streams.

var http = require('http');
var fs = require('fs');
http.createServer(function(req, res) {
// Specify html content type
res.writeHead(200, { 'Content-Type': 'text/html' });
// Replaced readFileSync and end with createReadStream.
// Read contents of index.htm and pipe to writeable stream res.
// Keeps buffer small, sends data a chunk at a time
// to res stream, thereby improving performance
// and avoiding synchronous operation.
fs.createReadStream(__dirname + '/index.htm').pipe(res);
}).listen(1337, '127.0.0.1');

These exercises helped me understand the basics of building Node servers and maybe it will be helpful to others getting started with Node. If anything is unclear or you have any suggestions or input, I would love to hear your feedback!