Computer scienceBackendNode.jsCore ConceptsInternal modulesHTTP serverhttp module

Creation of HTTP server

12 minutes read

The HTTP module has a createServer() method that allows us to read requests and send back responses. You can control routes, response headers, send cookies, and other basic HTTP features via createServer() method.

Creating server

To handle client requests, an HTTP server has to be created. Take a look closer at the code snipped below:

// server.js

const http = require('node:http'); // Importing module

const server = http.createServer((request, response) => { // server creation
  console.log(request);
  /* Your code here */
});

server.listen(8080); // listening on a specific port

First, you need to import the http core module into your code, so you could use the http module. Then declare a constant with the result of the http.createServer method. Inside the createServer method, you can handle requests and respective responses. At the end call the server.listen method and pass the port number. In this case, we've used the 8080 port, because developers often use 8080 and 8000 ports for developing purposes, since these ports are not commonly used by other services and programs.

The listen() method gets a number with a string, or an object, to create a listening point. By passing only the port number, the default host will be localhost (or 127.0.0.1) + port number.

// ...
server.listen(8080); // Will listen on http://localhost:8080/

server.listen(8080, '192.168.1.12'); // http://192.168.1.12:8080/

server.listen({
  port: 8080,
  host: 'localhost'
});

// http://localhost:8080

In the end, you'll get the server bounded to a specific port and host, so you can reach the server by localhost:8080 in a local browser. Then you'll see the terminal command output, which will return request object contents.

To return something to the browser, it needs to send the response:

const http = require('node:http');

const server = http.createServer((request, response) => {
	response.write('Hello, browser.');

	response.end();
});

server.listen(8080);

In the following snippet, you can see the two new functions of the response object. The write() function, which can write a response to the client's request. By default, it will return a text/plain media type, so it will be just a text. The response needs to be closed by the end() function. On this port and host, you can see the message.

Shorthand syntax

You can avoid constraints on the server by calling the server.listen() method. Instead of this, you can create a server and listen to it right away.

const http = require('node:http');

http.createServer((request, response) => {
	response.write('Hello, browser.');
	response.end();
}).listen(8080);

It will work as before.

Working with headers

To return other media types and response types, use the writeHead() method. You can also pass the response status code, message, and other options. In the next case, writeHead() has got a status code number and an object of a content type. You can use status codes for certain cases.

const http = require('node:http');

const server = http.createServer((request, response) => {
	response.writeHead(200, {'Content-Type': 'application/json'});

	response.write(JSON.stringify({ message: 'This is JSON response.' }));

	response.end();
});

server.listen(8080);

The code snippet above will return a JSON object with this content:

{"message":"This is JSON response."}

In the example below, you can change Content-Type to 'text/html' to send an HTML document.

const http = require('node:http');

const server = http.createServer((request, response) => {
	response.writeHead(200, {'Content-Type': 'text/html'});

	response.write('<h1>Hello</h1>');

	response.end();
});

server.listen(8080);

When opening the browser on the host and port, you will get an HTML page with a "Hello" heading. Try to send HTML or JSON, the browser will show you different results.

Notice that the method writeHead() can be called only once for one request before end(). Use this method before the write() method, or in other cases your headers won't show up.

// Headers won't be written

const server = http.createServer((request, response) => {
	response.write('<h1>Hello</h1>');

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

	response.end();
});
// Headers won't be written

const server = http.createServer((request, response) => {
	response.write('<h1>Hello</h1>');

	response.end();
    
	response.writeHead(200, {'Content-Type': 'text/html'});
});
// Will be set only the last Content-type - text/html

const server = http.createServer((request, response) => {
    response.writeHead(200, {'Content-Type': 'application/json'});
    response.writeHead(200, {'Content-Type': 'text/html'});

	response.write('<h1>Hello</h1>');

	response.end();
});

Another handy method you should know is setHeader(). This method can be called several times, and the result of this method will merge with other setHeader() methods before calling writeHead() and will be added to writeHead(). But setHeader() has another syntax as shown in the example below:

//...
response.setHeader('Content-Type', 'text/html');
response.writeHead(200);
//...
response.end();

Try to change the previous example, above the last one, and add headers with the setHeader() method. You will see that the result in the browser won't change because the content type was added by setHeader() and then merged with the status code in writeHead().

Adding cookies to the response

While you can write response headers, you also can tell the browser to store some cookies. To do that, add the setHeader() method and pass 'Set-Cookie' by the first parameter, then in the array pass all cookies that you want to set on the client.

const http = require('node:http');

const server = http.createServer((request, response) => {
	response.setHeader('Set-Cookie', ['userId=1', 'userToken=qwert123']);
	response.writeHead(200, {'Content-Type': 'text/html'});

	response.write('<h1>Hello</h1>');

	response.end();
});

server.listen(8080);

Then open http://localhost:8080/ in the browser. To make sure that the cookies were sent and written, first, open the dev-tools on the network tab, and click on the request. In the list of response headers, you will see Set-Cookie headers. Then open the storage tab, and check the cookies.

Setted cookies in the response header
Set cookies in the response header
Stored cookies in the cookies storage
Stored cookies in the cookies storage

By setting cookies, the backend can store information about the current user in the browser, so the client application can read the cookies and provide logic by the received information, or send this info back to the server to identify the user.

Reading request parameters

Some requests can have additional information. If these are GET requests, they may have URL parameters, if POST, they may have a request body. To read the request query by the GET request, you can use the URL module to parse the query string, then transform it into Object.

const http = require('node:http');
const url = require('node:url');

const server = http.createServer((request, response) => {
	const requestQueries = url.parse(request.url).query.split('&');
	const requestParams = Object.fromEntries(requestQueries.map((query) => query.split('=')));

	response.writeHead(200, {'Content-Type': 'application/json'});

	response.write(JSON.stringify(requestParams));

	response.end();
});

server.listen(8080);

Then open http://localhost:8080/?name=Bob&animal=Cat, and you will see a JSON response with the same data you've requested by URL query parameters. First, it needs to parse request.url, then split the query by &. In the next step, create an object via Object.fromEntries() function with a transformed array of key-value pairs that need to be split by =. As the result, you'll get the object with keys and values of the requested parameters. Then you can use this data as you wish in your projects, but in this case, it just turns back as the response. So ?name=Bob&animal=Cat will be converted to {name: "Bob", animal: "Cat"}.

In that case, when you access the URL, you might also get the error in the console, TypeError: Cannot read properties of null (reading 'split'), because the browser will send two requests. The first one is yours, with a GET request, and the second one is for favicon.ico. Since you don't have favicon.ico in that path, you will always get that error. You can handle it with the routing rules.
Query params as the JSON response
Query params as the JSON response

Basic routing

To handle a specific route or path it is possible to check the request.url parameter.

const http = require('node:http');

const server = http.createServer((request, response) => {
	if(request.url === '/animals') {
		response.writeHead(200, {'Content-Type': 'application/json'});

		response.write(JSON.stringify([
			'cat', 'dog', 'bird', 'elephant'
		]));

		response.end();
	};
});

server.listen(8080);

Now, the server will return an array of animals by requesting the http://localhost:8080/animals url. Other routes can't be reached.

This routing condition above will return an error when you want to pass query params(search params). So use url.parse to read the URL pathname, then create the condition. Instead of using if(request.url === '/animals'), use if(url.parse(request.url).pathname === '/animals').

Conclusion

Creating a server on Node.js is pretty simple. You can handle full control of incoming requests by reading the request and setting additional data for your response, by setting cookies, headers, and response body. In the body, you can return HTML pages, JSON, raw HTML data, or raw text information. This is the basis of the creation server on Node.js. The most popular framework to do backend applications on Node.js is Express.js, because it has tools for easily reading requests, waiting for them on different URLs, and many other handy features.

11 learners liked this piece of theory. 0 didn't like it. What about you?
Report a typo