How to create a web server in Node.js
Published: 2022-10-21 09:44:40

---
title: How to create a web server in Node.js
published: true
date: 2022-06-18 00:00:00 UTC
tags: 
canonical_url: https://www.mariokandut.com/how-to-create-a-web-server-with-node-js-core/
---

> 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.

## [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#create-a-minimal-web-server)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

### [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#1-basic-setup)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
```

### [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#2-create-a-basic-web-server)2. Create a basic web server

Now, create a file `server.js`.

```
touch server.js
```

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);
```

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.

### [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#3-add-routes-and-status-codes)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);
```

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);
```

We have destructured the `STATUS_CODES` object, which contains the status codes from the `http` module, see [docs](https://nodejs.org/dist/latest-v16.x/docs/api/http.html#httpstatus_codes). 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()"
```

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.

## [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#tldr)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](https://www.twitter.com/mariokandut)**.

If you want to know more about **[Node](https://www.mariokandut.com/tags/node)**, have a look at these [Node Tutorials](https://www.mariokandut.com/tags/node).

### [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#references-and-big-thanks)References (and Big thanks):

[NodeJS](https://nodejs.org),[NodeJS - http](https://nodejs.org/dist/latest-v16.x/docs/api/http.html),[JSNAD](https://trainingportal.linuxfoundation.org/),