{"id":265,"date":"2020-06-04T11:24:23","date_gmt":"2020-06-04T02:24:23","guid":{"rendered":"https:\/\/blog.wsd.sh\/?p=265"},"modified":"2020-06-04T11:32:48","modified_gmt":"2020-06-04T02:32:48","slug":"nodejs-book-chapter-4","status":"publish","type":"post","link":"https:\/\/blog.wsd.sh\/?p=265","title":{"rendered":"Nodejs Book: Chapter 4"},"content":{"rendered":"<p>In the last chapter we finished a simple file server with similar basic functionality<br \/>\nto Apache. That being said though, we have a long function to execute a very specific<br \/>\ntask. It would be ideal if we could create a module to separate that function to<br \/>\nmake our clean cleaner and shorter. The good news is that it&#8217;s entirely possible.<br \/>\nIf you recall, we already installed two libraries into our project, &#8220;mime&#8221; and<br \/>\n&#8220;async&#8221;, and the better news is that we can create our own libraries and modules<br \/>\nto be installed in the same way.<\/p>\n<p>We can copy and paste our function into it&#8217;s own source file for handling files.<\/p>\n<p>File: handle-file.js<\/p>\n<pre>\"use strict\";\n\nconst fs = require(\"fs\");\nconst mime = require(\"mime\");\nconst async = require(\"async\");\n\nconst PATH = \"public\";\n\nmodule.exports = function (req, res) {\n\n    fs.stat(PATH + req.url, function( err, stats ) {\n        if( err ) {\n            res.writeHead(404, {\"Content-Type\" : \"text\/plain\"});\n            return res.end(\"File Not Found\");\n        }\n\n        if( stats.isFile() ) {\n\n            res.writeHead(200, {\"Content-Type\" : mime.lookup(req.url) });\n            let stream = fs.createReadStream(PATH + req.url);\n            stream.pipe(res);\n\n        } else if( stats.isDirectory() ) {\n\n            if(req.url[req.url.length - 1] !== \"\/\") {\n\n                res.writeHead(302, { \"Location\" : req.url + \"\/\" });\n                return res.end();\n\n            }\n\n            fs.readdir(PATH + req.url, function(err, files) {\n                if(err) {\n                    throw err;\n                }\n\n                if(files.indexOf(\"index.html\") !== -1) {\n\n                    res.writeHead(200, {\"Content-Type\" : \"text\/html\" });\n                    let stream = fs.createReadStream(PATH + req.url + \"index.html\");\n                    stream.pipe(res);\n                    return;\n\n                }\n\n                let file_list = [];\n                let dir_list = [];\n\n                async.eachSeries(files, function(file, nextFile) {\n\n                    fs.stat(PATH + req.url + file, function( err, stats ) {\n                        if(err) {\n                            throw err;\n                        }\n\n                        if(stats.isFile()) {\n\n                            file_list.push(file);\n\n                        } else if(stats.isDirectory()) {\n\n                            if(file !== \".\") {\n                                dir_list.push(file);\n                            }\n\n                        }\n\n                        nextFile();\n                    });\n\n                }, function() {\n\n                    file_list.sort();\n                    dir_list.sort();\n\n                    res.writeHead(200, {\"Content-Type\" : \"text\/html\" });\n                    res.write(\"&lt;!DOCTYPE HTML&gt;\");\n                    res.write(\"&lt;html&gt;\");\n                    res.write(\"&lt;head&gt;\");\n                    res.write(\"&lt;meta charset=\\\"utf-8\\\"&gt;\");\n                    res.write(\"&lt;title&gt;Index&lt;\/title&gt;\");\n                    res.write(\"&lt;\/head&gt;\");\n                    res.write(\"&lt;body&gt;\");\n                    res.write(\"&lt;h1&gt;\"+req.url+\"&lt;\/h1&gt;\");\n                    res.write(\"&lt;ul&gt;\");\n\n                    dir_list.forEach(function(dir){\n\n                        res.write(\"&lt;li&gt;\");\n                        res.write(\"&lt;a href=\\\"\" + dir + \"\/\\\"&gt;\");\n                        res.write(dir + \"\/\");\n                        res.write(\"&lt;\/a&gt;\");\n                        res.write(\"&lt;\/li&gt;\");\n\n                    });\n\n                    file_list.forEach(function(file){\n\n                        res.write(\"&lt;li&gt;\");\n                        res.write(\"&lt;a href=\\\"\" + file + \"\\\"&gt;\");\n                        res.write(file);\n                        res.write(\"&lt;\/a&gt;\");\n                        res.write(\"&lt;\/li&gt;\");\n\n                    });\n\n                    res.write(\"&lt;\/ul&gt;\");\n                    res.write(\"&lt;\/body&gt;\");\n                    res.end(\"&lt;\/html&gt;\");\n\n                });\n\n            });\n\n        } else {\n\n            res.writeHead(404, {\"Content-Type\" : \"text\/plain\"});\n            return res.end(\"File Not Found\");\n\n        }\n\n    });\n\n}\n<\/pre>\n<p>Next we can run &#8220;`$ npm init&#8220;` in the directory we created our source file in<br \/>\nto create a package.json file describing the source file we created for Node to<br \/>\ninclude in other files. We will be asked to answer a few answers about our package<br \/>\nbut the result should look similar to the following.<\/p>\n<p>File: package.json<\/p>\n<pre>{\n  \"name\": \"handle-file\",\n  \"version\": \"1.0.0\",\n  \"description\": \"A simple function for serving static files from a \\\"public\\\" folder\",\n  \"main\": \"handle-file.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" &amp;&amp; exit 1\"\n  },\n  \"keywords\": [\n    \"handle\",\n    \"file\",\n    \"server\",\n    \"http\"\n  ],\n  \"author\": \"Benjamin Collins\",\n  \"license\": \"MIT\"\n}\n<\/pre>\n<p>But there&#8217;s one thing missing. Since we&#8217;re using the libraries &#8220;mime&#8221; and &#8220;async&#8221;<br \/>\n, we need to include that information into our package.json file. That will be<br \/>\ndone automatically is we run the command:<\/p>\n<pre>$ npm install async mime --save\n<\/pre>\n<p>After doing that, our package.json should shoud now have a &#8220;dependencies&#8221; attribute.<\/p>\n<pre>\"dependencies\": {\n  \"async\": \"^2.3.0\",\n  \"mime\": \"^1.3.4\"\n}\n<\/pre>\n<p>Now we&#8217;re almost ready to publish. Before doing so, we should create a &#8220;readme.md&#8221;<br \/>\nexplaining our code and what it does so other people can use it. Plus it&#8217;s always<br \/>\nhelpful if we forget. If you don&#8217;t already, you can create an account on<br \/>\nhttps:\/\/www.npmjs.com. Once you&#8217;ve done so, you can login via the command line<br \/>\nwith:<\/p>\n<pre>$ npm adduser\nUsername: [ username-here ]\nPassword: [ password-here ]\nEmail: (this IS public) your-email@domain.com\nLogged in as username-here on https:\/\/registry.npmjs.org\/.\n<\/pre>\n<p>Then we can publish our created module with:<\/p>\n<pre>$ npm publish .\n<\/pre>\n<p>We can now include our function to handle files like we did with &#8220;mime&#8221; and &#8220;async&#8221;<br \/>\nwith:<\/p>\n<pre>$ npm install file-handle\n<\/pre>\n<p>And our final code for this chapter is much shorter, looks like the following:<\/p>\n<p>File: file_server.js<\/p>\n<pre>\"use strict\";\n\nconst http = require(\"http\");\nconst handleFile = require(\"handle-file\");\n\nconst server = http.createServer();\nserver.on(\"request\", handleRequest);\nserver.listen(8080, handleListen);\n\nfunction handleRequest(req, res) {\n\n    handleFile(req, res);\n\n}\n\nfunction handleListen() {\n\n    console.log(\"Server is listening on port 8080\");\n\n}\n<\/pre>\n<p>And with that we&#8217;ve learned how to make modules and cleaned up our code. With a<br \/>\nbasic file server and cleaner code we are going to focus more on interaction with<br \/>\nclient web browsers in the next chapter.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the previous chapter, we implemented the functionality of a directory index where all of the files in a directory would be listed. As a result of that our code started to get long and messy, so in this chapter we introduce modules to implement specific functionality to organize our code.<\/p>\n","protected":false},"author":1,"featured_media":249,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_mi_skip_tracking":false},"categories":[4],"tags":[],"_links":{"self":[{"href":"https:\/\/blog.wsd.sh\/index.php?rest_route=\/wp\/v2\/posts\/265"}],"collection":[{"href":"https:\/\/blog.wsd.sh\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.wsd.sh\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.wsd.sh\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.wsd.sh\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=265"}],"version-history":[{"count":7,"href":"https:\/\/blog.wsd.sh\/index.php?rest_route=\/wp\/v2\/posts\/265\/revisions"}],"predecessor-version":[{"id":273,"href":"https:\/\/blog.wsd.sh\/index.php?rest_route=\/wp\/v2\/posts\/265\/revisions\/273"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.wsd.sh\/index.php?rest_route=\/wp\/v2\/media\/249"}],"wp:attachment":[{"href":"https:\/\/blog.wsd.sh\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=265"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.wsd.sh\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=265"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.wsd.sh\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=265"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}