Create a simple Node.js server

With Node.js, it's very easy to create a simple web server that can handle HTTP requests asynchronously. Node.js has a built in http module which we can be used to build a web server. This http module is very low-level and deals with stream handling and message parsing only. Let's create a simple Node.js server:

Create a file named server.js

   
   	touch server.js
   

To create a HTTP server, Node.js has a built in module called http which we need to import at the top of the file.

   
	const http = require('http');
   

And http module has a method called createServer to create an instance of http server.

   
   	const server = http.createServer((req, res) => {
		res.writeHead(200, { 'Content-Type': 'application/json' });
		res.end(JSON.stringify({
			data: 'Hello World!'
		}));
	});
   

Here, we can see, createServer method accepts another function as an argument. This type of function is referred to as requestListener function which is automatically added to the request event.  So, whenever a new request comes in, the server emits the request event with the request and response parameters and using those request and response objects, it executes a  requestListener function.

request - It contains all the information related to the client requests such as payload, header info, client info (browser info, ip address etc) and others.
response - It contains information that we want to send back to the clients including response payload and some header informations.

Let's simplify the code above even further:

   
   	const requestListenerFunc = (req, res) => {
	    res.writeHead(200, { 'Content-Type': 'application/json' });
	    res.end(JSON.stringify({
	        data: 'Hello World!'
	    }));
	}

	const server = http.createServer((req, res) => requestListenerFunc(req, res));
   

Here we define a variable called requestListenerFunc and assign it the arrow function from the createServer method. This makes code even more readable. We can still refactor the above code in the following way

   
	const server = http.createServer((req, res) => requestListenerFunc(req, res));
   

The above statement can be replaced with the following:

   
	const server = http.createServer(requestListenerFunc);
   

Now that we have created a http server and to make this http server able to serve client requests, we need to bind this http server to some host and port. Here in our case, since we are running this server in our own machine, we will use localhost as the host. In many of the examples, you will also see 127.0.0.1 used as the host as well. The default loopback IP address for localhost is 127.0.0.1 and you can see that by issuing following command as well.

   
   	ping localhost
   
localhost ping

Note that although localhost and 127.0.0.1 are used interchangeably in most places but localhost may not always resolve to 127.0.0.1 and can also point to some other ip addresses.

For port, we will use 3000 and any requests that is coming on http://localhost:3000 will have the requests forwarded to the below http server. So, we listen on the http server object that we created earlier using the port, host and the arrow function which logs the message once server is ready to listen for the client requests. So, this kind of arrow function is called callback function. You might wonder why it is called callback function. Well, a callback function is defined as a function which is executed only after another function has finished executing. The first argument in the callback function is always the error object and then others. In our case too, only after listen method of http server object is ready to listen to the client requests, then the arrow function is executed.


We will discuss callback function in detail in later articles and also have a look at a term called callback hell, which nowadays is not much common due to the introduction of async-await with the asynchronous code.  

   
   	server.listen(3000, "localhost", (err) => {
    	if (err) {
            return console.log('OOPS! Something went wrong: ', err)
        }
    	console.log(`Server is running on http://localhost:3000`);
	});
   

Lets execute this code to start our http web server. Issue the following command from the directory in which server.js file exists

   
   	node server.js
   

If everything goes right, then we will see the following output in the terminal

Node.js http server

Above http server listen for the client requests on the default route which is "/". Lets create a GET request to this default route using CURL command. You can also browse the url http://localhost:3000/ in the browser window as well

   
   	curl http://localhost:3000/
   
simple CURL GET command

To see the headers as well along with response payload:

   
   	curl  -v http://localhost:3000/
   
simple CURL GET command

You can see how easy it is to create a basic http web server using Node.js. Let's extend functionality of this server by exposing more endpoints.

request object exposes some properties which we will be using to further expand our server capabilities. Some of the properties that we will use are:

method: This gives us our HTTP methods. Some of the HTTP methods are GET, POST, PUT, DELETE, PATCH etc. We will discuss more on HTTP methods and REST protocol later.

url: This gives us the routes for which the request is coming

Lets modify the requestListenerFunc function to expose additional routes for get client requests:

   
	const requestListenerFunc = (req, res) => {
        const { url } = req;
        switch (req.method) {
            case 'GET':
                switch (url) {
                case "/":
                    res.writeHead(200, { 'Content-Type': 'application/json' });
                    res.end(JSON.stringify({
                        data: 'Hello World!'
                    }));
                    break;
                case "/hello":
                    res.writeHead(200, { 'Content-Type': 'application/json' });
                    res.end(JSON.stringify({
                        data: 'Say Hello!'
                    }));
                    break;
            break;
        }
    }
   

Now you can requests following routes:
http://localhost:3000/
http://localhost:3000/hello

Hitting this routes, we will have two different responses.

GET requests using CURL

Looking at the above code, You might wonder if it can be improved any further and make it more readable and clean. If you thought so, Great, you have a sense for good code quality. This is the first step towards clean, maintainable code.

Let's refactor the above code to make it more readable and clean.

   
		
	const requestListenerFunc = (req, res) => {
	    switch (req.method) {
	        case 'GET':
	            handleGetRequests(req, res);
	            break;
	    }
	}

	const handleGetRequests = (req, res) => {
	    const { url } = req;
	    switch (url) {
	        case "/":
	            sendResponseToClient(req, res, 'Hello World!', 200, 'application/json');
	            break;
	        case "/hello":
	            sendResponseToClient(req, res, 'Say Hello!', 200, 'application/json');
	            break;
	        default:
	            sendResponseToClient(req, res, 'Route not found', 404, 'application/json');
	            break;
	    }
	}

	const sendResponseToClient = (req, res, data, httpCode, contentType) => {
	    res.writeHead(httpCode, { 'Content-Type': contentType });
	    res.end(JSON.stringify({
	        data: data
	    }));
	}
   

Let's implement one route for HTTP POST request as well. We can also submit data with POST request.

   
   	const requestListenerFunc = (req, res) => {
	    switch (req.method) {
	        case 'GET':
	            handleGetRequests(req, res);
	            break;
	        case 'POST':
	            handlePostRequests(req, res);
	            break;
	    }
	}
   	const handlePostRequests = (req, res) => {
	    const size = parseInt(req.headers['content-length'], 10)
	    const buffer = Buffer.allocUnsafe(size)
	    var pos = 0

	    let data = '';
	    req.on('data', chunk => {
	        data += chunk;
	    });
	    req.on('end', () => {
	        const { url } = req;
	        switch (url) {
	            case "/hotel-info":
	                sendResponseToClient(req, res, JSON.parse(data), 200, 'application/json');
	                break;
	            }
	    });
	}
   

To make a POST request, we can use the following command:  

   
   	curl -X POST -d '{"name": "shrawan"}' http://localhost:3000/hotel-info
   
CURL POST request

Now that we have implemented GET and POST request using http module of Node.js, you can try for other HTTP methods as well. I will leave that to you. There are few other things that you can do to remove some code duplicates as well.

We can build an entire application without using any frameworks just like we did now. The problem with this approach is, it will be complex and time consuming for complex applications. There are many other concepts needed while building a complex application and without any frameworks, implementing those will be challenging as well as time consuming. For solving this, we have different frameworks based on Node.js which can be used with high efficiency and faster development speeds.

Among many frameworks, Express.js is one of the most popular Node.js framework which we are going to introduce in our next chapter.

Prev Chapter                                                                                          Next Chapter