Hope you are having a nice day. Today is Tuesday. Unless I am mistaken, it has been three weeks since Election Day and as far as I know the results for the presidential election are still up in the air. Hopefully we will find out the results in the next few days.
Next week is Thanksgiving Day. My wife and I were planning on having very few people over, but after further deliberations, we decided that we will skip and celebrate Thanksgiving 2021 with family and friends. Based on what is going on with COVID-19, it seems that a few vaccines will be made available early next year. By November 2021 we expect that most things would be back to normal; that is, the new normal.
Over the weekend my wife and I will be getting the main ingredients for the Thanksgiving 2020 celebration. We are planning on delivering some stuff to family members. No plans on getting together at all. Let’s keep it safe.
If you follow my blog, you are aware that I am currently reading “Get Programming with Node.js” by Jonathan Wexler. The book was published in 2019. Not sure if it was due to COVID-19, but several code snippets seem to be incorrect and incomplete. The good news is that you can visit a web site and download all the code and a PDF with the book contents. Have not read the PDF and might not do so. I prefer to read from a printed copy so I may easily underline and write notes. By the way, you will need a printed copy of the book if you wish to download the PDF and code.
It is better to read and experiment to make sure you get the most out of a book or video. I am doing so as I progress through the book.
By the way, as I was editing this post entry, I noticed that the blog has surpassed 4,000 subscribers. My sincere thanks to all that have subscribed!
In this capstone project, I made some changes to names and to the essence of the web site. If interested please get a copy of the book and get additional information about the project. I will show the steps I took. At this point the software is completed and does all the things required. That said; as you will see, there are some things that have left out for what appears to be a larger project. That is very typical of most projects. If this would be production code, you should remove all functions, variables, methods that are not used.
Microsoft Windows [Version 10.0.19041.630] (c) 2020 Microsoft Corporation. All rights reserved. # **** **** C:\Users\johnc>dir 11/10/2020 07:52 AM <DIR> . 11/10/2020 07:52 AM <DIR> .. 05/13/2020 02:07 PM 57 .angular-config.json 10/27/2020 02:16 PM <DIR> .atom 05/13/2020 02:03 PM <DIR> .config 10/27/2020 10:51 AM <DIR> .docker 09/22/2019 07:00 AM <DIR> .eclipse 03/15/2020 07:38 AM 59 .gitconfig 11/10/2020 07:59 AM <DIR> .lemminx 11/10/2020 07:46 AM <DIR> .m2 06/19/2020 07:40 AM 119 .node_repl_history 04/23/2020 10:11 AM <DIR> .p2 05/07/2020 07:11 AM 1,005,502 .pipwin 04/21/2020 07:07 AM <DIR> .pylint.d 09/22/2019 07:00 AM <DIR> .tooling 03/10/2020 02:53 PM <DIR> .vscode 11/01/2020 11:18 AM <DIR> 3D Objects 11/01/2020 11:18 AM <DIR> Contacts 04/08/2020 06:59 AM <DIR> ContainsCloseNums 02/17/2020 02:52 PM <DIR> Documents 11/12/2020 03:27 PM <DIR> Downloads 09/22/2019 06:57 AM <DIR> eclipse 09/22/2019 06:59 AM <DIR> eclipse-workspace_0 11/01/2020 11:18 AM <DIR> Favorites 10/27/2020 10:48 AM <DIR> getting-started 11/01/2020 11:18 AM <DIR> Links 11/01/2020 11:18 AM <DIR> Music 05/13/2020 02:49 PM <DIR> my-app 09/12/2019 03:36 PM <DIR> NCH Software Suite 07/16/2020 06:39 AM <DIR> node_modules 11/01/2020 11:14 AM <DIR> OneDrive 07/16/2020 06:39 AM 3,871 package-lock.json 05/04/2020 02:42 PM <DIR> pipwin 10/28/2020 01:21 PM <DIR> Postman 10/27/2020 03:23 PM <DIR> PostmanAgent 11/01/2020 11:18 AM <DIR> Saved Games 11/01/2020 11:18 AM <DIR> Searches 05/11/2020 02:04 PM <DIR> source 11/01/2020 11:18 AM <DIR> Videos 05/05/2020 12:30 PM <DIR> vimfiles 06/10/2020 09:50 AM <DIR> workspace0 08/17/2020 09:26 AM <DIR> workspace1 10/10/2020 07:04 AM <DIR> workspace2 11/06/2020 04:12 PM <DIR> workspace3 11/16/2020 02:01 PM <DIR> workspace4 05/05/2020 12:51 PM 3,281 _viminfo # **** **** C:\Users\johnc>cd workspace4 # **** **** C:\Users\johnc\workspace4>dir 11/16/2020 02:01 PM <DIR> . 11/16/2020 02:01 PM <DIR> .. 11/03/2020 08:26 AM <DIR> AngleBetweenClockHands 10/15/2020 09:37 AM <DIR> BackspaceStringCompare 10/30/2020 07:59 AM <DIR> BSTToGST 10/21/2020 09:46 AM <DIR> CoinChange 10/21/2020 03:36 PM <DIR> CoinChange2 11/16/2020 07:40 AM <DIR> CombinationsJava 11/04/2020 03:39 PM <DIR> DatesInJava 11/12/2020 06:49 AM <DIR> DemoForRESTAPI 11/04/2020 08:37 AM <DIR> DoublesInJava 10/19/2020 10:23 AM <DIR> FibonacciDynamicComputing 11/16/2020 02:03 PM <DIR> FirstWebApp <=== 11/10/2020 11:37 AM <DIR> javafunctional 11/07/2020 07:59 AM <DIR> JavaStreams 10/22/2020 06:20 AM <DIR> LRUCache 11/11/2020 11:12 AM <DIR> MaxLenConcatStrUniqueChars 10/26/2020 07:30 AM <DIR> PathWithMinimumEffort 11/13/2020 08:17 AM <DIR> second_server 11/13/2020 08:18 AM <DIR> simple_server # **** folder for this project **** C:\Users\johnc\workspace4>cd FirstWebApp # **** initialize the project **** C:\Users\johnc\workspace4\FirstWebApp>npm init This utility will walk you through creating a package.json file. It only covers the most common items, and tries to guess sensible defaults. See `npm help init` for definitive documentation on these fields and exactly what they do. Use `npm install <pkg>` afterwards to install a package and save it as a dependency in the package.json file. Press ^C at any time to quit. package name: (firstwebapp) nonas_kitchen version: (1.0.0) description: Capstone project unit 1 entry point: (index.js) main.js test command: git repository: keywords: author: John Canessa license: (ISC) About to write to C:\Users\johnc\workspace4\FirstWebApp\package.json: { "name": "nonas_kitchen", "version": "1.0.0", "description": "Capstone project unit 1", "main": "main.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "John Canessa", "license": "ISC" } Is this OK? (yes) y # **** **** C:\Users\johnc\workspace4\FirstWebApp>dir 11/16/2020 02:07 PM <DIR> . 11/16/2020 02:07 PM <DIR> .. 11/16/2020 02:07 PM 243 package.json 11/16/2020 02:03 PM <DIR> public 11/16/2020 02:03 PM <DIR> views # **** **** C:\Users\johnc\workspace4\FirstWebApp>type package.json { "name": "nonas_kitchen", "version": "1.0.0", "description": "Capstone project unit 1", "main": "main.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "John Canessa", "license": "ISC" } # **** save http-status-code package as an application dependency **** C:\Users\johnc\workspace4\FirstWebApp>npm i http-status-codes -S npm notice created a lockfile as package-lock.json. You should commit this file. npm WARN nonas_kitchen@1.0.0 No repository field. + http-status-codes@2.1.4 added 1 package from 1 contributor and audited 1 package in 0.603s found 0 vulnerabilities # **** **** C:\Users\johnc\workspace4\FirstWebApp>npm i bootstrap -S npm WARN bootstrap@4.5.3 requires a peer of jquery@1.9.1 - 3 but none is installed. You must install peer dependencies yourself. npm WARN bootstrap@4.5.3 requires a peer of popper.js@^1.16.1 but none is installed. You must install peer dependencies yourself. npm WARN nonas_kitchen@1.0.0 No repository field. + bootstrap@4.5.3 added 1 package from 2 contributors and audited 2 packages in 0.532s 1 package is looking for funding run `npm fund` for details found 0 vulnerabilities # **** **** C:\Users\johnc\workspace4\FirstWebApp>type package.json { "name": "nonas_kitchen", "version": "1.0.0", "description": "Capstone project unit 1", "main": "main.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "John Canessa", "license": "ISC", "dependencies": { "bootstrap": "^4.5.3", <=== "http-status-codes": "^2.1.4" } }
The screen capture starts by getting to the folder I chose for the web application. You can name it as you please.
Once in the folder we need to initialize the project with npm init. The utility will prompt for some information. I names the project “nonas_kitchen” which is not the name used in the book. In addition, the entry point for the application was set changed to “main.js” instead of index.js.
When done, we display the contents of the folder. We have a couple new folders and a new package.json file. The contents of the file are then displayed.
We install the http-status-codes package because it allows us to use their definitions instead of defining the codes ourselves.
Based on the contents of the book, I was under the impression that we were going to use bootstrap in this project. Unless I missed something, we did not.
After the installation we check the package.json file. We now require bootstrap.
// **** exports **** module.exports = { html: { "Content-Type": "text/html" }, text: { "Content-Type": "text/plain" }, js: { "Content-Type": "text/js" }, jpg: { "Content-Type": "image/jpg" }, png: { "Content-Type": "image/png" }, css: { "Content-Type": "text/css" } };
This file contains a list of content types supported by our application. Some of the values are not used at this time.
// **** **** const port = 3000; // **** required modules **** http = require("http"); httpStatus = require("http-status-codes"); router = require("./router"); contentTypes = require("./contentTypes"); utils = require("./utils"); // **** register individual routes with the router module **** router.get("/", (req, res) => { res.writeHead(httpStatus.OK, contentTypes.htm); utils.getFile("views/index.html", res); }); router.get("/recipes.html", (req, res) => { res.writeHead(httpStatus.OK, contentTypes.html); utils.getFile("views/recipes.html", res); }); router.get("/contact.html", (req, res) => { res.writeHead(httpStatus.OK, contentTypes.html); utils.getFile("views/contact.html", res); }); router.get("/graph.png", (req, res) => { res.writeHead(httpStatus.OK, contentTypes.png); utils.getFile("public/images/graph.png", res); }); router.get("/people.jpg", (req, res) => { res.writeHead(httpStatus.OK, contentTypes.jpg); utils.getFile("public/images/people.jpg", res); }); router.get("/product.jpg", (req, res) => { res.writeHead(httpStatus.OK, contentTypes.jpg); utils.getFile("public/images/product.jpg", res); }); router.get("/nonas_kitchen.css", (req, res) => { res.writeHead(httpStatus.OK, contentTypes.css); utils.getFile("public/css/nonas_kitchen.css", res); }); router.get("/bootstrap.css", (req, res) => { res.writeHead(httpStatus.OK, contentTypes.css); utils.getFile("public/css/bootstrap.css", res); }); router.get("/nonas_kitchen.js", (req, res) => { res.writeHead(httpStatus.OK, contentTypes.js); utils.getFile("public/js/nonas_kitchen.js", res); }); router.post("/", (req, res) => { res.writeHead(httpStatus.OK, contentTypes.html); utils.getFile("views/thanks.html", res); }); // ???? ???? console.log(`<<< router.get: ${router.get}`); console.log(`<<< router.post: ${router.post}`); // **** create server and listen on the specified port **** http.createServer(router.handle).listen(port); // ???? ???? console.log(`<<< web application listening on port : ${port}`);
This is the main.js file. We specify to use port 3000. We then specify the required modules. Note that some modules come from existing packages, while others we will have to write in order to make them available to our application. Make sure you understand the folder structure for the project. The next step is to define the routes our application will support. All but one is associated with GET. There is only one route associated with POST. Not all the routes are used at this time. You can try a route that is not implemented to see what happens. We display some messages to make sure all is well during execution. We create the application which listens on port 3000. A message is display that our application is up and running.
{
"name": "nonas_kitchen",
"version": "1.0.0",
"description": "Capstone project unit 1",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "John Canessa",
"license": "ISC",
"dependencies": {
"bootstrap": "^4.5.3",
"http-status-codes": "^2.1.4"
}
}
This is the contents of the package.json file. I have nothing more to add to it at this point.
// **** required modules **** const httpStatus = require("http-status-codes"); contentTypes = require("./contentTypes"); utils = require("./utils"); // **** **** const getJSONString = (obj) => { return JSON.stringify(obj, null, 2); } // **** routes **** const routes = { "GET": {}, "POST": {} }; // **** functions to handle requests **** exports.handle = (req, res) => { // ???? ???? console.log(`<<< req.method: ${getJSONString(req.method)}`); console.log(`<<< req.url: ${getJSONString(req.url)}`); console.log(`<<< req.headers: ${getJSONString(req.headers)}`); // **** **** try { routes[req.method][req.url](req,res); } catch (e) { res.writeHead(httpStatus.OK, contentTypes.html); utils.getFile("views/error.html", res); } }; // **** **** exports.get = (url, action) => { routes["GET"][url] = action; }; // **** **** exports.post = (url, action) => { routes["POST"][url] = action; }
This is the routes.js file. It is used to dispatch requests based on the verb (GET or POST) and the URL.
The getJSONString() function is used to display information about requests.
The routes are used to track all GET and POST requests. These were populated by the main.js module.
When a request arrives, we display some information. This is only needed to debug and experiment. We then dispatch the request.
Note that the entries for GET and POST in our routes are exported so they can be accessed in the main.js module.
// **** required modules **** const fs = require("fs"); httpStatus = require("http-status-codes"); contentTypes = require("./contentTypes"); // **** exports **** module.exports = { // **** **** getFile: (file, res) => { fs.readFile(`./${file}`, (error, data) => { if (error) { res.writeHead(httpStatus.INTERNAL_SERVER_ERROR, contentTypes.html); res.end("Error encountered while serving content!"); } res.end(data); }); } };
The utils.js file is used to respond to requests with files. Please try specifying files that do not exist and replace files with empty versions.
# **** **** C:\Users\johnc\workspace4\FirstWebApp>dir /S 11/16/2020 04:47 PM <DIR> . 11/16/2020 04:47 PM <DIR> .. 11/16/2020 03:05 PM 384 contentTypes.js 11/17/2020 02:19 PM 2,160 main.js 11/16/2020 04:47 PM <DIR> node_modules 11/16/2020 04:47 PM 642 package-lock.json 11/17/2020 02:19 PM 329 package.json 11/16/2020 02:03 PM <DIR> public 11/17/2020 02:14 PM 1,027 router.js 11/17/2020 02:20 PM 531 utils.js 11/16/2020 05:09 PM <DIR> views Directory of C:\Users\johnc\workspace4\FirstWebApp\public 11/16/2020 02:03 PM <DIR> . 11/16/2020 02:03 PM <DIR> .. 11/16/2020 02:02 PM <DIR> css 11/16/2020 02:02 PM <DIR> images 11/16/2020 02:03 PM <DIR> js 0 File(s) 0 bytes Directory of C:\Users\johnc\workspace4\FirstWebApp\views 11/16/2020 05:43 PM 1,237 contact.html 11/16/2020 05:31 PM 987 error.html 11/16/2020 05:20 PM 1,251 index.html 11/16/2020 05:31 PM 1,224 recipes.html 11/17/2020 02:16 PM 935 thanks.html
This illustrated the folder structure we used for this project. In the next post we will be using the Express.js web framework which will simplify and automate many manual operations we had to perform for this project. I will cover the next Capstone project in this blog.
[nodemon] restarting due to changes... [nodemon] starting `node main.js` <<< router.get: (url, action) => { routes["GET"][url] = action; } <<< router.post: (url, action) => { routes["POST"][url] = action; } <<< web server listening on port : 3000 <<< req.method: "GET" <<< req.url: "/" <<< req.headers: { "host": "localhost:3000", "connection": "keep-alive", "cache-control": "max-age=0", "dnt": "1", "upgrade-insecure-requests": "1", "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36", "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "sec-fetch-site": "none", "sec-fetch-mode": "navigate", "sec-fetch-user": "?1", "sec-fetch-dest": "document", "accept-encoding": "gzip, deflate, br", "accept-language": "en-US,en;q=0.9,es;q=0.8,it-IT;q=0.7,it;q=0.6" } <<< req.method: "GET" <<< req.url: "/bootstrap.css" <<< req.headers: { "host": "localhost:3000", "connection": "keep-alive", "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36", "dnt": "1", "accept": "text/css,*/*;q=0.1", "sec-fetch-site": "same-origin", "sec-fetch-mode": "no-cors", "sec-fetch-dest": "style", "referer": "http://localhost:3000/", "accept-encoding": "gzip, deflate, br", "accept-language": "en-US,en;q=0.9,es;q=0.8,it-IT;q=0.7,it;q=0.6" } <<< req.method: "GET" <<< req.url: "/nonas_kitchen.css" <<< req.headers: { "host": "localhost:3000", "connection": "keep-alive", "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36", "dnt": "1", "accept": "text/css,*/*;q=0.1", "sec-fetch-site": "same-origin", "sec-fetch-mode": "no-cors", "sec-fetch-dest": "style", "referer": "http://localhost:3000/", "accept-encoding": "gzip, deflate, br", "accept-language": "en-US,en;q=0.9,es;q=0.8,it-IT;q=0.7,it;q=0.6" } <<< req.method: "GET" <<< req.url: "/people.jpg" <<< req.headers: { "host": "localhost:3000", "connection": "keep-alive", "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36", "dnt": "1", "accept": "image/avif,image/webp,image/apng,image/*,*/*;q=0.8", "sec-fetch-site": "same-origin", "sec-fetch-mode": "no-cors", "sec-fetch-dest": "image", "referer": "http://localhost:3000/", "accept-encoding": "gzip, deflate, br", "accept-language": "en-US,en;q=0.9,es;q=0.8,it-IT;q=0.7,it;q=0.6" } <<< req.method: "GET" <<< req.url: "/favicon.ico" <<< req.headers: { "host": "localhost:3000", "connection": "keep-alive", "pragma": "no-cache", "cache-control": "no-cache", "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36", "dnt": "1", "accept": "image/avif,image/webp,image/apng,image/*,*/*;q=0.8", "sec-fetch-site": "same-origin", "sec-fetch-mode": "no-cors", "sec-fetch-dest": "image", "referer": "http://localhost:3000/", "accept-encoding": "gzip, deflate, br", "accept-language": "en-US,en;q=0.9,es;q=0.8,it-IT;q=0.7,it;q=0.6" }
This is a screen capture of the console running our web application.
I just refreshed the web browser pointing to “localhost:3000” and all the activity that followed between the Chore web browser and the web application.
Hope you enjoyed solving this problem as much as I did. The entire code for this project can be found in my GitHub repository.
If you have comments or questions regarding this, or any other post in this blog, or if you would like for me to help out with any phase in the SDLC (Software Development Life Cycle) of a project associated with a product or service, please do not hesitate and leave me a note below. If you prefer, send me a private e-mail message. I will reply as soon as possible.
Keep on reading and experimenting. It is the best way to learn, become proficient, refresh your knowledge and enhance your developer toolset!
One last thing, many thanks to all 4,190 subscribers to this blog!!!
Keep safe during the COVID-19 pandemic and help restart the world economy.
John
E-mail: john.canessa@gmail.com