DEV Community

Mario
Mario

Posted on • Originally published at mariokandut.com on

How to create a web server in Node.js

This article is based on Node v16.15.1.

This article shows how to create a web server in Node.js with the core APIs. We are going to use the http module. In a real world scenario this is not recommended, and a web framework should be used. This article is only demonstrating that this is possible without a framework.

Create a minimal web server

A minimum viable web server would have the following requirements:

  • Responds to HTTP requests based on a HTTP verb (for example. GET)
  • Responds to request based on a given route
  • Responds with 404 status code, if route isn't found.
  • Sets applicable headers (for example: Content-Type:)

Next steps

  1. Basic Setup
  2. Create a basic web server
  3. Add routes and status codes

1. Basic Setup

Node.js has to be installed. Use the node version manager nvm to install Node.

Create a folder http-web-server and cd into it.

mkdir http-web-server
cd http-web-server
Enter fullscreen mode Exit fullscreen mode

2. Create a basic web server

Now, create a file server.js.

touch server.js
Enter fullscreen mode Exit fullscreen mode

And then copy the following code into it.

'use strict';
const http = require('http');
const PORT = process.env.PORT || 3000;

const helloWorld = `<html>
  <head>
    <style>
     body { background: white; margin: 1.5rem }
     h1 { color: red; font-family: sans-serif }
    </style>
  </head>
  <body>
    <h1>Hello World</h1>
  </body>
</html>`;

const server = http.createServer((req, res) => {
  res.setHeader('Content-Type', 'text/html');
  res.end(helloWorld);
});

server.listen(PORT);
Enter fullscreen mode Exit fullscreen mode

In the code above, we have used the http module to create a server with the createServer method. The createServer method is passed a function with two objects in, the request object and the response object. This function is called every time the server receives a request and the objects are created for every request. In the function we set the header to Content-Type: text/html and return the constant helloWorld to return the HTML string. The response object is a writable stream , because in the node core it inherits from stream.Stream), and calling end writes the content and closes the connection. The listen method binds the defined PORT to the object created from createServer.

Now execute the code with node server.js and open a browser tab with http://localhost:3000. You should see a Hello World in red on a white background.

The Node process will not exit by itself, because the created server keeps the process open.

3. Add routes and status codes

Our example is not meeting the criteria. Currently, for any route the same response is given, we don't return any status codes and have no error handling.

Let's update our server.js with a response based on route.

'use strict';
const http = require('http');
const url = require('url');

const PORT = process.env.PORT || 3000;

const helloWorld = `<html>
  <head>
    <style>
     body { background: white; margin: 1.5rem }
     h1 { color: red; font-family: sans-serif }
    </style>
  </head>
  <body>
    <h1>Hello World</h1>
  </body>
</html>`;

const root = `<html>
  <head>
    <style>
     body { background: white; margin: 1.5rem }
     h1 { color: black; font-family: sans-serif }
     a { color: green; text-decoration: underline; }
    </style>
  </head>
  <body>
    <h1>ROOT</h1>
    <a href='/hello'>Hello</a>
  </body>
</html>`;

const server = http.createServer((req, res) => {
  res.setHeader('Content-Type', 'text/html');
  const { pathname } = url.parse(req.url);

  if (pathname === '/') {
    res.end(root);
    return;
  }

  if (pathname === '/hello') {
    res.end(helloWorld);
    return;
  }
});

server.listen(PORT);
Enter fullscreen mode Exit fullscreen mode

Now with the help of the url module we are parsing the request url and returning a different HTML string based on the route. The parse method in the url module turns a URL string into an object containing various segments of the URL.

Restart your node server. When you navigate to http://localhost:3000/hello you should see in black ROOT and if you go to http://localhost:3000/hello you should see hello world red.

Now let's add the status codes and error handling for route not found and wrong HTTP verb.

'use strict';
const http = require('http');
const url = require('url');

const PORT = process.env.PORT || 3000;
const { STATUS_CODES } = http;

const helloWorld = `<html>
  <head>
    <style>
     body { background: white; margin: 1.5rem }
     h1 { color: red; font-family: sans-serif }
    </style>
  </head>
  <body>
    <h1>Hello World</h1>
  </body>
</html>`;

const root = `<html>
  <head>
    <style>
     body { background: white; margin: 1.5rem }
     h1 { color: black; font-family: sans-serif }
     a { color: green; text-decoration: underline; }
    </style>
  </head>
  <body>
    <h1>ROOT</h1>
    <a href='/hello'>Hello</a>
  </body>
</html>`;

const server = http.createServer((req, res) => {
  res.setHeader('Content-Type', 'text/html');
  const { pathname } = url.parse(req.url);

  // check http method
  if (req.method !== 'GET') {
    res.statusCode = 405;
    res.end(STATUS_CODES[res.statusCode] + '\r\n');
    return;
  }

  if (pathname === '/') {
    res.end(root);
    return;
  }

  if (pathname === '/hello') {
    res.end(helloWorld);
    return;
  }

  // no route found
  res.statusCode = 404;
  res.end(STATUS_CODES[res.statusCode] + '\r\n');
});

server.listen(PORT);
Enter fullscreen mode Exit fullscreen mode

We have destructured the STATUS_CODES object, which contains the status codes from the http module, see docs. If the request method is not GET, we are returning the status code 405 (Method not allowed) and end the response with the status message from the key-values of STATUS_CODES.

If the route is not / or /hello, we return status code 404 (Page not found) and end the response with the applicable message from STATUS_CODES.

Restart your node server and check if the routes are handled correctly. For testing the wrong http verb, only GET is supported in our server, open postman or insomnia or use the following code to make a POST request with the http module

node -e "http.request('http://localhost:3000', {method: 'POST'}, (res) => res.pipe(process.stdout)).end()"
Enter fullscreen mode Exit fullscreen mode

In any case the response should be Method not allowed.

The status code 200 (OK) is the default status code, hence, there is no need to set it for the routes / and /hello.

We have now met all criteria and have created a basic web server.

This approach is very rigid and cumbersome, especially when you try to add more functionality. Node.js frameworks like Express or Fastify enables us to write a more flexible, maintainable and declarative way.

TL;DR

  • It is possible to create a web server with the core modules of Node.js, but the code becomes rigid and unmanageable.
  • In a real-world scenario you would write web servers with Node.js frameworks.

Thanks for reading and if you have any questions , use the comment function or send me a message @mariokandut.

If you want to know more about Node, have a look at these Node Tutorials.

References (and Big thanks):

NodeJS,NodeJS - http,JSNAD,

Top comments (0)