Use Node core APIs to create a minimal web server
Posted
Updated
Europe’s developer-focused job platform
Let companies apply to you
Developer-focused, salary and tech stack upfront.
Just one profile, no job applications!
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.
💰 The Pragmatic Programmer: journey to mastery. 💰 One of the best books in software development, sold over 200,000 times.
A minimum viable web server would have the following requirements:
Content-Type:
)Next steps
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
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.
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.
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.
but the code becomes rigid and unmanageable.
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.
Never miss an article.