Adding routes and logic to a pure Node.js server

rahul kumar mandal
10 min readFeb 10, 2019
  • Right now, we have a web server that understands exactly one route that understands exactly one route that’s for the slash or index and it will return that’s for the slash or index and it will return four-zero-four for anything beyond.
const http = require('http');
const url = require('url');

function handler(req, res) {
const parsedUrl =url.parse(req.url, true);
if(parsedUrl.pathname === '/'){
res.writeHead(200, {'Content-type':'text/plain'});
res.write('Hello, I am a webserver !');
res.end();
}else {
res.writeHead(404, {'Content-type':'text/plain'});
res.end();
};



}

const server = http.createServer(handler);

server.listen(3000);

That’s not much functionality, so let’s expand this a little bit. For instance, let’s create a route that will return us the current time. So, I add a new block with an else-if enter and look into parsed url dot path name enter and look into parsed url dot path name and if it is slash time I will respond with, and if it is slash time I will respond with, and if it is slash time I will respond with, again so, the header 200 from before again so, the header 200 from before and then I will use res dot write and there I will create a new date object and there I will create a new date object and with to-string it will give me and with to-string it will give me the current date and in the end I will do a res dot end.

the current date and in the end I will do a res dot end. If I see some cascade of ifs, like here. If I see some cascade of ifs, like here. Let’s make sure that in the end of each if we really do a return to emphasize that we really do a return to emphasize that the execution of this function really ends here

const http = require('http');
const url = require('url');
function handler(req, res) {
const parsedUrl =url.parse(req.url, true);
if(parsedUrl.pathname === '/'){
res.writeHead(200, {'Content-type':'text/plain'});
res.write('Hello, I am a webserver !');
res.end();
}else if (parsedUrl.pathname === '/time'){
res.writeHead(200, {'Content-type': 'text/plain'});
res.write(new Date().toString());
return res.end();
}else {
res.writeHead(404, {'Content-type':'text/plain'});
res.end();
};
}const server = http.createServer(handler);server.listen(3000);

and let’s try our time route out now. and let’s try our time route out now. So, I call node pure node server dot js and head over to the browser and call slash time and as you see it will return with the current time.

Next let’s create a url that greets us with our name and we want to pass in the name as so called clear parameter, means I would as so called clear parameter, means I would do something like slash hello and then do something like slash hello and then question mark and name equals Rahul question mark and name equals Rahul and right now this will return in a four-zero-four. So, let’s change that, so add a new block So, let’s change that, so add a new block with an else-if parsed url dot path name equals slash hello with an else-if parsed url dot path name equals slash hello with an else-if parsed url dot path name equals slash hello and if so we should get the name in the parsed url objectand if so we should get the name in the parsed url object in query so we can define const name in query so we can define const nameequals parsed url dot query dot name equals parsed url dot query dot name equals parsed url dot query dot name and let’s add some error checking here, because maybe someone did forget to pass in the name.

So, I will add if not name and a proper way to respond So, I will add if not name and a proper way to respond to invalid inputs is and error code four-zero-zero. to invalid inputs is and error code four-zero-zero. For that request to add res write head 400, For that request to add res write head 400, so I’m copying it simply over from the four-zero-four error handler. So, it’s a bad request if no name was passed. In any other case let’s go with the block over from In any other case let’s go with the block over from the time parse and for the res write I will use the time parse and for the res write I will use es-six string interpolation and for that I use es-six string interpolation and for that I use the tick character and I will return the tick character and I will return hello dollar curly brackets and in there the name.

hello dollar curly brackets and in there the name.

const http = require('http');
const url = require('url');
function handler(req, res) {
const parsedUrl =url.parse(req.url, true);
if(parsedUrl.pathname === '/'){
res.writeHead(200, {'Content-type':'text/plain'});
res.write('Hello, I am a webserver !');
res.end();
}else if (parsedUrl.pathname === '/time'){
res.writeHead(200, {'Content-type': 'text/plain'});
res.write(new Date().toString());
return res.end();
}else if(parsedUrl.pathname === '/hello'){
const name = parsedUrl.query.name;
if(!name) {
res.writeHead(400, {'Content-type': 'text/plain'});
return res.end();
}
res.writeHead(200, {'Content-type':'text/plain'});
res.write(`Hello ${name}`);
return res.end();
}else {
res.writeHead(404, {'Content-type':'text/plain'});
res.end();
};
}const server = http.createServer(handler);server.listen(3000);

Let’s give this a try and so I’m running the web server again and let’s reload this name equals Rahul query here again and let’s reload this name equals Rahul query here and we get a hello Rahul

and if we omit the query string we get an error 400. omit the query string we get an error 400.

In modern web applications we often also want to use dynamic urls. Let’s say we want to create the route something like user and then some user name like Rahul user and then some user name like Rahul to pull up the user profile of Rahul.

to pull up the user profile of Rahul. So, right now this of course is a four-zero-four and we want to create a handler for that.

Now matching through the path name exactly does not work anymore and we have to find a way to extract the actual user name. So the dynamic part from the url. So, let’s see how we could do that, for that I add another block with another else-if. for that I add another block with another else-if.And now I’m using parsed url dot path name and now And now I’m using parsed url dot path name and now And now I’m using parsed url dot path name and now the java script function starts with, to test if the the java script function starts with, to test if the path name starts with slash user slash.

path name starts with slash user slash. In India I will use a so called regular expression instruct regex, regex are basically a science on it’s own instruct regex, regex are basically a science on it’s own and they are used to match patterns against strings. If you are not familiar with regex that’s totally no problem just trust me that what I’m doing now is correct and we won’t need it throughout the rest of the course.

It’s really just for demonstration purposes what we are doing here. So, add const regex new regex, that’s a java script class So, add const regex new regex, that’s a java script class that creates regex for me and in there I add my regex definition to start with a backslash I add my regex definition to start with a backslash and a slash and I want to match against user and a slash and I want to match against user and followed by another slash and some arbitrary string and followed by another slash and some arbitrary string of variable length that is done with a dot plus.

of variable length that is done with a dot plus. And next I want to execute this regex, And next I want to execute this regex, or run it against our path name. So, I add const matches equals So, I add const matches equals regex dot exec parsed url dot path name. regex dot exec parsed url dot path name. regex dot exec parsed url dot path name. Now, I again want to add some error checking. For that I will first copy over the error check from above For that I will first copy over the error check from above and here I will test for if there is no matches arrayand here I will test for if there is no matches array at all or no matches or no index one at all or no matches or no index one at all or no matches or no index one we will again return a 400 error.

we will again return a 400 error. In any other we can reuse much of what we did before In any other we can reuse much of what we did before and I copy this over and I will now reply with user profile of matches with the index one. reply with user profile of matches with the index one. reply with user profile of matches with the index one.

const http = require('http');
const url = require('url');
function handler(req, res) {
const parsedUrl =url.parse(req.url, true);
if(parsedUrl.pathname === '/'){
res.writeHead(200, {'Content-type':'text/plain'});
res.write('Hello, I am a webserver !');
res.end();
}else if (parsedUrl.pathname === '/time'){
res.writeHead(200, {'Content-type': 'text/plain'});
res.write(new Date().toString());
return res.end();
}else if(parsedUrl.pathname === '/hello'){
const name = parsedUrl.query.name;
if(!name) {
res.writeHead(400, {'Content-type': 'text/plain'});
return res.end();
}
res.writeHead(200, {'Content-type':'text/plain'});
res.write(`Hello ${name}`);
return res.end();
}else if(parsedUrl.pathname.startsWith('/user/')) {
const regex = new RegExp('\/user\/(.+)');
const matches = regex.exec(parsedUrl.pathname);
if(!matches || !matches[1]) {
res.writeHead(400, {'Content-type':'text/plain'});
res.end();
}
res.writeHead(200, {'Content-type': 'text/plain'});
res.write(`userprofile of ${matches[1]}`);
return res.end();
}else {
res.writeHead(404, {'Content-type':'text/plain'});
res.end();
};
}const server = http.createServer(handler);server.listen(3000);

So, let’s rerun that and let’s see what this gives us.

We called the route user if I remember correctly so I load slash user slash Rahul and I get user profile of Rahul. and I get user profile of Rahul.Lastly, let’s say we want to return specific http header Lastly, let’s say we want to return specific http header that contains the current date on the server. This header should be always set, so we can set it right at the beginning of the handler function which would mean that it’s cache all that is executed which would mean that it’s cache all that is executed regardless of the parse so here I add res dot set header regardless of the parse so here I add res dot set header x dash server dash date and this shall be new date.

const http = require('http');
const url = require('url');
function handler(req, res) {
const parsedUrl =url.parse(req.url, true);
res.setHeader('x-server-date', new Date());if(parsedUrl.pathname === '/'){
res.writeHead(200, {'Content-type':'text/plain'});
res.write('Hello, I am a webserver !');
res.end();
}else if (parsedUrl.pathname === '/time'){
res.writeHead(200, {'Content-type': 'text/plain'});
res.write(new Date().toString());
return res.end();
}else if(parsedUrl.pathname === '/hello'){
const name = parsedUrl.query.name;
if(!name) {
res.writeHead(400, {'Content-type': 'text/plain'});
return res.end();
}
res.writeHead(200, {'Content-type':'text/plain'});
res.write(`Hello ${name}`);
return res.end();
}else if(parsedUrl.pathname.startsWith('/user/')) {
const regex = new RegExp('\/user\/(.+)');
const matches = regex.exec(parsedUrl.pathname);
if(!matches || !matches[1]) {
res.writeHead(400, {'Content-type':'text/plain'});
res.end();
}
res.writeHead(200, {'Content-type': 'text/plain'});
res.write(`userprofile of ${matches[1]}`);
return res.end();
}else {
res.writeHead(404, {'Content-type':'text/plain'});
res.end();
};
}const server = http.createServer(handler);server.listen(3000);

x dash server dash date and this shall be new date. Let’s save that and head over to our browser again, Let’s save that and head over to our browser again, but how can we now look at the headers? For that I will open chrome developer tools. So, I’m clicking here on those dots on the right side and under more tools you find chrome developer tools, on my Mac I can also open it with alt, command, and i.

There we go, let’s reload the page There we go, let’s reload the page to get the network communication here on the network tab and when I click on the network tab and on the first tab I have headers and let’s look at the response headers and we see that we have x server dateresponse header now containing the current date.

As you can see although we only covered a few types requests our code is already rather complex and convoluted.

We don’t even get handles, things like cookies or static files or post requests or just more complexness throughout. The next video we will try to implement the same logic with Xpress.

Previous…………………………………………………………………….Next

--

--