Node.js | Basics of Node.js Scripts

Node.js creates an http.Server object from the http object to build a server. It also sends and receives data through request and response objects. This article explains the basic use of these objects.

Basic Script and the http Object

Let us explain the basics of a Node.js script by looking at the script created earlier.

var http = require('http');
 
var server = http.createServer();
server.on('request', doRequest);
server.listen(process.env.PORT, process.env.IP);
console.log('Server running!');
 
// Request processing
function doRequest(req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.write('Hello World\n');
    res.end();
}

Loading the http Object

var http = require('http');

The first thing to do is load the required libraries. Use the require(..) function. Specify the object name to import as the argument, and the object is loaded and returned. Assign it to a variable and use it.

Here, the http object is loaded. As its name suggests, it collects HTTP-related features. A server object is created from it.

Creating a Server Object

var server = http.createServer();

Calling createServer() on the http object creates an http.Server object. This is the part that becomes the Node.js server. Prepare this object, configure what is needed, and run it as a server.

Many examples write it in the following style.

http.createServer(function(x){
    ...necessary processing...
}).listen(x);

This writes the request handling function as the argument to createServer, then continues with listen. This is valid, but because everything is written as one block, it can be hard to understand at first. For now, remember that createServer can be called without arguments, and necessary processing can be set afterward.

Setting Request Processing

server.on('request', doRequest);

The http.Server object provides various events and methods for registering their processing. The on method registers processing for a specified event. The first argument is the event name, and the second argument is the processing function.

Here, the function doRequest is assigned to the request event. request occurs when the http.Server object receives a client request, meaning it is where you put the server response processing for browser access.

Starting Listening

server.listen(1234);

Once the http.Server object is ready, execute the listen method. The server enters a waiting state, and when a client request arrives, it can receive and process it. The argument specifies the port number. Other arguments can specify host name, backlog, and callback, but for now it is enough to remember that the first argument is the port number.

Console Output

console.log('Server running!');

After listening starts, a message is output to the console. console is an object for console operations, and log outputs logs. Log output is useful for light debugging.

Function for Request Processing

The flow for creating, preparing, and starting the http.Server object is now clear. What remains is the content of the processing performed when a request is received.

In the previous example, the function doRequest was defined and registered to the request event with on. The work performed by this request event is the processing for a client request.

The function is defined in the following form.

function functionName(req, res) {
    ...... necessary processing ......
}

Two objects are passed as arguments.

  • request: The first argument is the request object. It is an http.IncomingMessage object and collects features related to the client request.
  • response: The second argument is the response object. It is an http.ServerResponse object and collects features related to the response returned from the server to the client.

Use these request and response objects to build request processing.

Sending Header Information

res.writeHead(200, {'Content-Type': 'text/plain'});

writeHead is a response object method that writes header information to the response and sends it. The first argument specifies the status code, and the second argument groups header information as an associative array.

Here, {'Content-Type': 'text/plain'} sets the value of the Content-Type header to text/plain. This tells the client that the returned content is plain text.

Sending Content

res.write('Hello World\n');

In HTTP, content is written as the body after the header information. The response object’s write method sends that content. The value specified as the argument is written as body content.

write can be called multiple times. Calling it writes content, but does not yet finish the response, so more content can be appended with additional write calls.

Finishing Content Output

res.end();

When content output is complete, call end on the response to finish output. You can also pass content as an argument, in which case it writes that value and then finishes.

With end, response processing ends and the request processing is complete. With writeHead, write, and end, you can write all content returned to the client.

Loading Files with the fs Object

You may wonder whether all HTML must be written with write. Preparing the entire page as a string in a script is not realistic.

For a Web application, page content should be prepared as HTML files and read for display. This is done with the fs object.

Getting the fs Object

var fs = require('fs');

As with the http object, read the fs object and assign it to a variable.

Reading a File

fs.readFile(file path, encoding, callback function);

This loads a file. It does not return the read data directly because it runs asynchronously.

Reading a file takes time. If processing waited until loading finished, other users might have to wait as well. Therefore, Node.js starts the read operation and immediately proceeds to the next work. The file is loaded in the background. When loading finishes, the callback function prepared in advance is called. A function called later after work completes is called a callback function.

The first argument to readFile is the path of the file to read, the second is the encoding name, and the third is the callback function executed after loading completes.

Displaying an HTML File

Create a file named hello.html in the same location as the Node.js script.

hello.html

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <title>sample</title>
</head>
<body>
    <header>
        <h1 id="h1">Sample Page</h1>
    </header>
    <div role="main">
        <p>This is a Node.js example.</p>
    </div>
</body>
</html>

Then write the script.

sampleapp.js

var http = require('http');
var fs = require('fs');
 
var server = http.createServer();
server.on('request', doRequest);
server.listen(1234);
console.log('Server running!');
 
// Request processing
function doRequest(req, res) {
    fs.readFile('./hello.html', 'UTF-8', 
        function(err, data) {
            res.writeHead(200, {'Content-Type': 'text/html'});
            res.write(data);
            res.end();
        });
}

The contents of hello.html will be displayed. Here, doRequest loads hello.html and displays it when a request is received. The callback function receives an error object as the first argument and the loaded text as the second argument. If loading fails, handle it with something like if (err) {...}.

It is important that writeHead, write, and end are all executed inside the callback. Because readFile is asynchronous, calling res.end() outside the callback would finish the response before file loading completes.

Changing Part of HTML with a Script

You can treat HTML like a simple template by putting special placeholder values in it, loading the file, replacing those values, and outputting the result.

hello.html

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <title>@title@</title>
</head>
<body>
    <header>
        <h1 id="h1">@title@</h1>
    </header>
    <div role="main">
        <p>@content@</p>
    </div>
</body>
</html>

@title@ and @content@ are placeholder values for replacement.

sampleapp.js

var http = require('http');
var fs = require('fs');
 
var server = http.createServer();
server.on('request', doRequest);
server.listen(1234);
console.log('Server running!');
 
// Request processing
function doRequest(req, res) {
    var number = Math.floor(Math.random() * 3);
    fs.readFile('./hello.html', 'UTF-8', 
        function(err, data) {
            var title = ["Page A", "Page B", "Page C"];
            var content = ["This was made as an example.",
                "This is another content item.",
                "This is the last content item."];
            var data2 = data.
                replace(/@title@/g, title[number]).
                replace(/@content@/g, content[number]);
            res.writeHead(200, {'Content-Type': 'text/html'});
            res.write(data2);
            res.end();
        });
}

Here, after reading the text, replace changes the placeholders. @title@ and @content@ are replaced with randomly selected values from the title and content arrays. Each time the page is refreshed, randomly selected text appears.

This lets small values be embedded into HTML from a script. However, this is still not very flexible. To generate and manipulate display more generally, the next step is to use a template engine.