In Part II of this series: Making a Simple HTTP Server with Node.js – Part II we learned how to use the “path” and “file system” modules. By leveraging these Node.js modules, we were able to read the path of the HTTP request that our server received, and read from the file system of the web server. We also learned about the “__dirname” keyword, which provides an abstract reference to the folder in which the currently executing JavaScript file exists.
That version of our HTTP server simply checked to see if the request was for the file “index.html”, and if so, served it up. That was progress over Part I, but still not robust enough.
In this article, we will expand our Node.js HTTP server so that the following services are provided:
- If an asset is requested, and it exists, it will be returned
- If an asset is not specified, then “index.html” will be returned
- If an asset is requested, and it does not exist, our custom 404.html page will be returned
We will make every effort to keep our three-step paradigm, which will, we hope, continue to illustrate that creating an HTTP web server with Node.js is at its core, quite simple. What we have to take a closer look at, is the fact that, as we ask more of our little web server, we have to roll up our sleeves and write the code that provides that very functionality.
So the good news is: It’s just more JavaScript. Yay!
Example # 1
1 2 3 4 5 6 7 8 9 10 11 |
//step 1) require the modules we need var http = require('http'), path = require('path'), fs = require('fs'); //step 2) create the server http.createServer(requestHandler) //step 3) listen for an HTTP request on port 3000 .listen(3000); |
In Example #1, we have the three-step core of our HTTP web server. We have already discussed each step in detail, so let’s just quickly re-cap:
Step # 1: Use the require() method to gain access to the Node.js modules that we need
Step # 2: Use the createServer() method of the HTTP module, and pass it a reference to our “requestHandler” function
Step # 3: Listen for a request on port # 3000
Simple, simple, simple.
Next, let’s look at two functions: our updated requestHandler() function, and then our new getFile() function.
Example # 2
1 2 3 4 5 6 7 8 9 10 11 12 13 |
//a helper function to handle HTTP requests function requestHandler(req, res) { var fileName = path.basename(req.url) || 'index.html', localFolder = __dirname + '/public/', page404 = localFolder + '404.html'; //call our helper function //pass in the path to the file we want, //the response object, and the 404 page path //in case the requestd file is not found getFile((localFolder + fileName),res,page404); }; |
In Example # 2, we first look at the request, and if no file was requested (i.e. the user simply typed http://somedomain.com into their browser), then we prepare to serve up “index.html”.
As we learned in Part II, the __dirname keyword provides an abstract reference to the folder in which the currently executing JavaScript file resides. We then create a variable named “page404”, which will provide a reference to our custom “404 / Not Found” page, should we need it.
Now we have everything we need, so we call the getFile() function, passing it the path and name of the asset we want (i.d. index.html), the response object, and then the reference to our custom 404 page.
Example # 3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
//helper function handles file verification function getFile(filePath,res,page404){ //does the requested file exist? fs.exists(filePath,function(exists){ //if it does... if(exists){ //read the file, run the anonymous function fs.readFile(filePath,function(err,contents){ if(!err){ //if there was no error //send the contents with the default 200/ok header res.end(contents); } else { //for our own troubleshooting console.dir(err); }; }); } else { //if the requested file was not found //serve-up our custom 404 page fs.readFile(page404,function(err,contents){ //if there was no error if(!err){ //send the contents with a 404/not found header res.writeHead(404, {'Content-Type': 'text/html'}); res.end(contents); } else { //for our own troubleshooting console.dir(err); }; }); }; }); }; |
In Example # 3, there are some new things happening. First, we use the exists() method of the file system object that was returned by the “fs” module, and assigned to our variable: “fs”. This method takes two arguments: a path to the file, and an anonymous function. That anonymous function takes one argument: “exists” (call it whatever you like). That single argument provides a helpful “truthy/falsy” flag against which we can test.
So if you take a moment to think about this, you’ll find that it’s quite cool: baked into the Node.js “fs” module is a method that simply tells us whether or not a named file exists. This is a perfect example of the brilliance of Node.js modules. Imagine how much heavy lifting you’d have to do if you needed to provide this kind of implementation yourself. Fortunately, someone did it for you. And that module can be used over and over again… a zillion more times if you like.
So moving along, if the “exists” argument returns a “truthy” value, we use the fs.readFile() method to literally read the physical file from the local file system. We have a little error checking to make sure that the file read operation did not fail, and if it did not fail, we send the contents of that file back to the user.
If the requested file was not found (i.e. the “exists()” method told us that the named file does not exist), then we serve up the custom 404.html file that we still have a reference to.
Example # 4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
//step 1) require the modules we need var http = require('http'), path = require('path'), fs = require('fs'); //helper function handles file verification function getFile(filePath,res,page404){ //does the requested file exist? fs.exists(filePath,function(exists){ //if it does... if(exists){ //read the fiule, run the anonymous function fs.readFile(filePath,function(err,contents){ if(!err){ //if there was no error //send the contents with the default 200/ok header res.end(contents); } else { //for our own troubleshooting console.dir(err); }; }); } else { //if the requested file was not found //serve-up our custom 404 page fs.readFile(page404,function(err,contents){ //if there was no error if(!err){ //send the contents with a 404/not found header res.writeHead(404, {'Content-Type': 'text/html'}); res.end(contents); } else { //for our own troubleshooting console.dir(err); }; }); }; }); }; //a helper function to handle HTTP requests function requestHandler(req, res) { var fileName = path.basename(req.url) || 'index.html', localFolder = __dirname + '/public/', page404 = localFolder + '404.html'; //call our helper function //pass in the path to the file we want, //the response object, and the 404 page path //in case the requestd file is not found getFile((localFolder + fileName),res,page404); }; //step 2) create the server http.createServer(requestHandler) //step 3) listen for an HTTP request on port 3000 .listen(3000); |
Example # 4 simply puts all our code examples together, to provide some context.
Summary
In this article we expanded our simple Node.js HTTP web server. We leveraged the exists() method of the “fs” (file system) object, to determine if the requested file actually exists, and provided logic that handles cases in which the requested file does not exist.
Helpful Links for the Node.js path and fs Modules
https://github.com/jprichardson/node-fs-extra
http://docs.nodejitsu.com/articles/file-system/how-to-write-files-in-nodejs
Thank you so much, this was really helpful!
Hi Anita,
Thanks for your feedback!
Kevin
Thanks, very helpful!
Hi Loi,
Thanks for your feedback!
Kevin
This saved my day. I was able to load AngularJS app inside electron. Otherwise it did not worked through file:///