Nona’s Kitchen – JavaScript

Good morning! It is Sunday of the Thanksgiving Day 2020 weekend. Time seems to be passing by quite quickly. Earlier this morning I was reading an article regarding the Pfizer COVID-19 vaccine. It seems that there are still some items that need to be addressed before that vaccine becomes readily available in the USA. Accordingly to the article, we should achieve herd immunity summer 2021. Let’s keep our fingers crossed. In addition there are many other laboratories that are about the same stage as Pfizer. It is safe and reasonable not to depend on a single source.

While I was about to finish editing this document and getting ready to post it, my wife called me to go grocery shopping to Trader Joe’s. On weekends, unless I get up earlier (around 04:00 AM), I typically have time to work on a single 2-hour block. I can extend the block if needed, but if I am doing something with my wife, that tends to put an end to work on that day.

To avoid crowds and get to the store as soon as possible, I just run upstairs, got ready and left home.

When we got back, I helped putting the groceries away, went down to my office and turned off the computers that were on. Back in the kitchen, I prepared two triple (that was not a typo) espressos, sat down with my wife in the living room and we called our kids to chat on-line.

For lunch we had turkey sandwiches with ciabatta bread and potato chips. After doing the dishes, we sat down to watch on Netflix “The Queen’s Gambit”. This was our first miniseries. We both recommend watching it. The only issue was a set of racial comments in the script made by the actress Moses Ingram. In my opinion they were completely out of context and had nothing to do with the main plot.

Now let’s get down to the subject of this post.

As you know I have been reading the book “Get Programming with Node.js” by Jonathan Wexler. This post covers the capstone project associated with lesson 12. It covers Express.js, routing, serving dynamic and static pages and error handling. If this is of interest to you, I suggest you get a copy of the book. It is recommended by the author to read and experiment with the code. As you know I am a believer of reading and experimenting in order to learn.

# **** open console ****
C:\Users\johnc>cd workspace5

# **** ****
C:\Users\johnc\workspace5>dir
11/28/2020  07:48 AM    <DIR>          .
11/28/2020  07:48 AM    <DIR>          ..
11/21/2020  11:15 AM    <DIR>          ConstructBSTFromPreorderTraversal
11/25/2020  08:17 AM    <DIR>          ContinuousSubarraySum
11/28/2020  07:42 AM    <DIR>          NonasKitchen		<=== folder for this project
11/19/2020  05:36 PM    <DIR>          PrintListInReverse
11/24/2020  07:01 AM    <DIR>          ValidateBST

# **** ****
C:\Users\johnc\workspace5>cd NonasKitchen

# **** initialize project ****
C:\Users\johnc\workspace5\NonasKitchen>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: (nonaskitchen)
version: (1.0.0)
description: Nona's Kitchen web site (no database)
entry point: (index.js) main.js
test command:
git repository:
keywords:
author: John Canessa
license: (ISC)
About to write to C:\Users\johnc\workspace5\NonasKitchen\package.json:

{
  "name": "nonaskitchen",
  "version": "1.0.0",
  "description": "Nona's Kitchen web site (no database)",
  "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\workspace5\NonasKitchen>dir
11/28/2020  07:52 AM    <DIR>          .
11/28/2020  07:52 AM    <DIR>          ..
11/28/2020  07:52 AM               256 package.json

# **** ****
C:\Users\johnc\workspace5\NonasKitchen>type package.json
{
  "name": "nonaskitchen",
  "version": "1.0.0",
  "description": "Nona's Kitchen web site (no database)",
  "main": "main.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "John Canessa",
  "license": "ISC"
}

# **** after editing the package.json file ****
C:\Users\johnc\workspace5\NonasKitchen>type package.json
{
  "name": "nonaskitchen",
  "version": "1.0.0",
  "description": "Nona's Kitchen web site (no database)",
  "main": "main.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node main.js"		<==== to use:  npm start
  },
  "author": "John Canessa",
  "license": "ISC"
}

I selected a location in my Windows 10 computer and created the NonasKitchen folder using Windows Explorer. In retrospect I should have used the mkdir command on the console (sorry about that).

I used the npm init command to initialize our new project. Responded to all the prompts and accepted them. Remember that you can always edit the package.json manually. Note that I followed the recommendation by Jon Wexler to name the entry point main.js.

# **** install express ****
C:\Users\johnc\workspace5\NonasKitchen>npm install express --save
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN nonaskitchen@1.0.0 No repository field.

+ express@4.17.1
added 50 packages from 37 contributors and audited 50 packages in 5.897s
found 0 vulnerabilities

# **** install ejs ****
C:\Users\johnc\workspace5\NonasKitchen>npm install ejs --save
npm WARN nonaskitchen@1.0.0 No repository field.

+ ejs@3.1.5
added 15 packages from 8 contributors and audited 65 packages in 2.225s
found 0 vulnerabilities

# **** install http-status-codes 
C:\Users\johnc\workspace5\NonasKitchen>npm install http-status-codes --save
npm WARN nonaskitchen@1.0.0 No repository field.

+ http-status-codes@2.1.4
added 1 package from 1 contributor and audited 66 packages in 0.81s
found 0 vulnerabilities

# **** ****
C:\Users\johnc\workspace5\NonasKitchen>npm install express-ejs-layouts --save
npm WARN nonaskitchen@1.0.0 No repository field.

+ express-ejs-layouts@2.5.0
added 1 package from 1 contributor and audited 67 packages in 1.138s
found 0 vulnerabilities

# **** ****
C:\Users\johnc\workspace5\NonasKitchen>type package.json
{
  "name": "nonaskitchen",
  "version": "1.0.0",
  "description": "Nona's Kitchen web site (no database)",
  "main": "main.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node main.js"
  },
  "author": "John Canessa",
  "license": "ISC",
  "dependencies": {
    "ejs": "^3.1.5",					<==== new dependency
    "express": "^4.17.1",				<==== new dependency
    "express-ejs-layouts": "^2.5.0",	<==== new dependency
    "http-status-codes": "^2.1.4"		<==== new dependency
  }
}

We need some packages for our web server. We first install Express.js. We specify the –save flag because we want to flag that this module is required by our web server.

The same holds true for the Embedded JavaScript templating package.

We then install the http-status-codes package.

Last but not least, we install the express-ejs-layouts package.

Just to check what we have installed, we display the contents of the package.json file. In it we see the four dependencies for the npm packages we just installed.

The next step is to create a directory structure for our project. We want to use the MVP approach so we should separate files to follow such design and make each file perform related tasks.

C:\Users\johnc\workspace5\NonasKitchen>dir
11/28/2020  08:07 AM    <DIR>          .
11/28/2020  08:07 AM    <DIR>          ..
11/28/2020  08:07 AM    <DIR>          node_modules
11/28/2020  08:07 AM            18,984 package-lock.json
11/28/2020  08:07 AM               428 package.json

# **** ****
C:\Users\johnc\workspace5\NonasKitchen>mkdir controllers

# **** ****
C:\Users\johnc\workspace5\NonasKitchen>mkdir public

# **** ****
C:\Users\johnc\workspace5\NonasKitchen>mkdir views

# **** change directory to public ****
C:\Users\johnc\workspace5\NonasKitchen>cd public

# **** ****
C:\Users\johnc\workspace5\NonasKitchen\public>mkdir css

# **** ****
C:\Users\johnc\workspace5\NonasKitchen\public>mkdir images

# **** ****
C:\Users\johnc\workspace5\NonasKitchen\public>mkdir js

# **** up one directory ****
C:\Users\johnc\workspace5\NonasKitchen\public>cd ..

# **** project directory structure (contents of node_modules have been edited out) ****
C:\Users\johnc\workspace5\NonasKitchen>dir /S /B
C:\Users\johnc\workspace5\NonasKitchen\controllers
C:\Users\johnc\workspace5\NonasKitchen\node_modules
C:\Users\johnc\workspace5\NonasKitchen\package-lock.json
C:\Users\johnc\workspace5\NonasKitchen\package.json
C:\Users\johnc\workspace5\NonasKitchen\public
C:\Users\johnc\workspace5\NonasKitchen\views
C:\Users\johnc\workspace5\NonasKitchen\public\css
C:\Users\johnc\workspace5\NonasKitchen\public\images
C:\Users\johnc\workspace5\NonasKitchen\public\js

I display the initial contents of the root folder for our project. I then create the necessary folders. When done I display the updated folder structure for our project.

The next step will be to populate the folders as needed.

/**
 * C:\Users\johnc\workspace5\NonasKitchen\main.js
 */

// **** the code should be executed in "strict mode" ****
"use strict";

// **** ****
const express = require("express");

// **** required controllers ****
const homeController = require("./controllers/homeController");
const errorController = require("./controllers/errorController");

// **** ****
const layouts = require("express-ejs-layouts");

// **** instantiate the express application ****
const app = express();

// **** set application to use EJS ****
app.set("view engine", "ejs");

// **** port to listen on ****
app.set("port", process.env.PORT || 3000);

// **** tell express to use body-parser to process url-encoded parameters ****
app.use(
    express.urlencoded({
        extended: false
    })
);

// **** tell express to use JSON parameters ****
app.use(express.json());

// **** set application to use the layout module ****
app.use(layouts);

// **** serve directly individual assets from the specified folder ****
app.use(express.static("public"));

// **** route for home page ****
app.get("/", (req, res) => {
    res.render("index");
});

// **** routes ****
app.get("/courses", homeController.showCourses);
app.get("/contact", homeController.showSignUp);
app.post("/contact", homeController.postedSignUpForm);

// **** specify how to handle errors ****
app.use(errorController.pageNotFoundError);
app.use(errorController.internalServerError);

// **** start application listening on the specified port ****
app.listen(app.get("port"), () => {

    // **** log that the server is up and running ****
    console.log(`<<< server running on: http://localhost:${app.get("port")}`);
});

Note that in this and many other JavaScript files I will be following the suggestion of the book author and start each source file with the “use strict” directive. Helps us avoid writing sloppy JavaScript code.

I believe that I have documented the code rather well. The different statements specify the required modules. We create the necessary variables. Specify which features from some packages we are going to use. We then move on to the routes. We specify the route for the home page followed by the routes to the three main pages of our application. We are using two error handlers. Will see what they do shortly.

We then start our application on the specified port and log a message to indicate that all is well so far and the web server is up and running.

/**
 * C:\Users\johnc\workspace5\NonasKitchen\errorController.js
 */

// **** the code should be executed in "strict mode" ****
"use strict";

// **** ****
const httpStatus = require("http-status-codes");

// **** handle requests not previously handled ****
exports.pageNotFoundError = (req, res) => {
    let errorCode = httpStatus.NOT_FOUND;
    res.status(errorCode);
    res.render("error");
};

// **** handle internal errors ****
exports.internalServerError = (error, req, res, next) => {
    let errorCode = httpStatus.INTERNAL_SERVER_ERROR;
    console.log(`ERROR occurred: ${error.stack}`);
    res.status(errorCode);
    res.send(`${errorCode} | Sorry, our application is taking a nap!`);
};

The error handlers should be used when a page is not found or when an internal server error is detected. It is always important to inform the user when something goes wrong. Make sure that the error is adequate for your audience. Very few users might be fine with an error code, but most would prefer some language (an possibly an image) to indicate that the software ran into an issue.

/**
 * C:\Users\johnc\workspace5\NonasKitchen\homeController.js
 */

// **** the code should be executed in "strict mode" ****
"use strict";

// **** courses ****
var courses = [
  {
    title: "Event Driven Cakes",
    cost: 50
  },
  {
    title: "Asynchronous Artichoke",
    cost: 25
  },
  {
    title: "Object Oriented Orange Juice",
    cost: 10
  }
];

// **** callback function for courses ****
exports.showCourses = (req, res) => {
    res.render("courses", {

        // **** pass the courses array to the view ****
        offeredCourses: courses
    });
};

// **** callback function for contact page ****
exports.showSignUp = (req, res) => {

    // **** ****
    res.render("contact");
};

// **** callback function after submitting sign up form ****
exports.postedSignUpForm = (req, res) => {

    // **** ****
    res.render("thanks");
};

The homeController.js file defines a variable with a list of course names and associated costs. This variable can be accessed from other modules. In specific we are separating view from control. Imagine if we are constantly having to modify the list of courses offered and associated costs. Without this array we would have to edit one or more HTML files. Always try to have a single source for the information you use. That way you will not ran into issues when data referring to the same object is different.

<!- C:\Users\johnc\workspace5\NonasKitchen\index.ejs ->

<div class="container">

  <div class="col-sm-6">
    <img src="images/people.jpg" alt="" style="width:500px;height:500px;">
  </div>

  <div class="col-sm-6">
    <h1>Welcome!!!</h1>
    <p> Please check out all the cool new courses by our Nona.</p>

    <p> If you want to see a new course, click <a href="./courses">the courses page</a> and explore!</p>
    <p> When you see a course you like you can <a href="./contact">contact us to join! </a> </p>
    
    <p> Soon you'll be cooking in your own home.</p>
  </div>

</div>

This file represents the default page for our server. It displays an image, some information and contains two links. You can try modifying the links for the application to fail and display an error page.

<!- C:\Users\johnc\workspace5\NonasKitchen\contact.ejs ->

<div class="container">

  <div class="col-sm-6">
    <img src="images/graph.png" alt="" style="width:500px;height:500px;">
  </div>

  <div class="col-sm-6">
    <h1>Reach out to Nona!!!</h1>
    <p>Please enter your name and email if you are interested in learning more:</p>

    <form action="/contact" method="post">
      <label for="name">Name:</label>
      <input type="text" name="name" value="">
      <br>
      <label for="email">Email:</label>
      <input type="email" name="email" value="">
      <br><br>
      <input class="button" type="submit" value="submit">
    </form>

    <p> We will get back to you as soon as possible.</p>
    <p> We might not sell your personal information (ha, ha, ha)</p>
  </div>

</div>
<!- C:\Users\johnc\workspace5\NonasKitchen\courses.ejs ->

<div class="container">

  <div class="col-sm-6">
    <img src="images/fruit.jpg" alt="" style="width:500px;height:500px;">
  </div>

  <div class="col-sm-6">
    <h1 class="title">Learn to cook cutting edge food!!!</h1>
    <h1>Our Courses</h1>

    <% offeredCourses.forEach(course => { %>
    <h5>
      <%= course.title %>
    </h5>
    <span>$
      <%= course.cost %> </span>
    <% }); %>

  </div>

</div>

The courses.ejs page uses the variable that we defined in the homeController.js file. This view will reflect the changes in the list we import. This feature is provided by express dynamic code features.

<!- C:\Users\johnc\workspace5\NonasKitchen\error.ejs ->

<div class="container">

  <div class="col-sm-6">
    <img src="/images/cat.jpg" alt="Error cat" style="width:500px;height:500px;">
  </div>

  <div class="col-sm-6">
    <h1>Oops! Something went wrong, or the page you are looking for is not available.</h1>
    <p> Go to
      <a href="/">home!</a>
    </p>
  </div>
  
</div>

This page is displayed when our application runs into an error.

<!- C:\Users\johnc\workspace5\NonasKitchen\layout.ejs ->

<!DOCTYPE html>
<html>

<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=0.8, maximum-scale=1">
	<title>Nona's Kitchen</title>
	<link rel="shortcut icon" type="image/png" href="/images/icon.png" />
	<link rel="stylesheet" href="/css/bootstrap.css">
	<link rel="stylesheet" href="/css/nonas_kitchen.css">
</head>

<body>
	<div id="nav">
		<div class="col-sm nav-align">
			<h1 id="title">Nona's Kitchen</h1>
		</div>
		<div class="col-sm nav-align">
			<a href="/">
				<span class="button">Home</span>
			</a>
			<a href="/courses">
				<span class="button">Courses</span>
			</a>
			<a href="/contact">
				<span class="button">Contact</span>
			</a>
		</div>
	</div>

	<%- body %>
</body>

</html>

This file contains the layout for our application. This page will be used by the three pages in the web server application. It displays a header with the navigation buttons.

Of interest is the <%- body %> line. This will be filed with the body of the contents specified when the user clicks a link in the navigation. This page acts as a template for the three pages we have in our application.

<!- C:\Users\johnc\workspace5\NonasKitchen\thanks.ejs ->

<div class="container">

  <div class="col-sm-6">
    <img src="/images/graph.png" alt="" style="width:500px;height:500px;">
  </div>

  <div class="col-sm-6">
    <h2>Thank you for submitting your valuable and private information!!!</h2>
    <p> Go to
      <a href="/">home!</a>
    </p>
  </div>

</div>

After a user populates and submits the form with their name and email address, our server responds with this page.

# **** directory contents when done ****
C:\Users\johnc\workspace5\NonasKitchen>dir /S /B
C:\Users\johnc\workspace5\NonasKitchen\controllers
C:\Users\johnc\workspace5\NonasKitchen\main.js
C:\Users\johnc\workspace5\NonasKitchen\package-lock.json
C:\Users\johnc\workspace5\NonasKitchen\package.json
C:\Users\johnc\workspace5\NonasKitchen\public
C:\Users\johnc\workspace5\NonasKitchen\views
C:\Users\johnc\workspace5\NonasKitchen\controllers\errorController.js
C:\Users\johnc\workspace5\NonasKitchen\controllers\homeController.js
C:\Users\johnc\workspace5\NonasKitchen\public\404.html
C:\Users\johnc\workspace5\NonasKitchen\public\css
C:\Users\johnc\workspace5\NonasKitchen\public\images
C:\Users\johnc\workspace5\NonasKitchen\public\js
C:\Users\johnc\workspace5\NonasKitchen\public\images\cat.jpg
C:\Users\johnc\workspace5\NonasKitchen\public\images\fruit.jpg
C:\Users\johnc\workspace5\NonasKitchen\public\images\graph.png
C:\Users\johnc\workspace5\NonasKitchen\public\images\icon.png
C:\Users\johnc\workspace5\NonasKitchen\public\images\people.jpg
C:\Users\johnc\workspace5\NonasKitchen\views\contact.ejs
C:\Users\johnc\workspace5\NonasKitchen\views\courses.ejs
C:\Users\johnc\workspace5\NonasKitchen\views\error.ejs
C:\Users\johnc\workspace5\NonasKitchen\views\index.ejs
C:\Users\johnc\workspace5\NonasKitchen\views\layout.ejs
C:\Users\johnc\workspace5\NonasKitchen\views\thanks.ejs

This is the current structure of the folders and files we have in our project. Note that the images folder has been populated with some JPEG files. Such files were provided by the author of the book. You can use any other file. I was considering replacing the cat with a picture of my dogs. I am a dog person.

# **** to load the node_modules folder with all dependencies ****
C:\Users\johnc\workspace5\NonasKitchen>npm install
npm WARN nonaskitchen@1.0.0 No repository field.

added 67 packages from 46 contributors and audited 67 packages in 3.124s
found 0 vulnerabilities

# **** start the web server with nodemon ****
C:\Users\johnc\workspace5\NonasKitchen>nodemon
[nodemon] 2.0.6
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node main.js`
<<< server running on: http://localhost:3000

As you will notice in the GitHub repository associated with this post, the node_modules folder has been removed. The reason is that it takes a lot of space and with time newer versions of the modules that may provide faster, better and safer code might become available. Using the first command you should be able to restore the missing folder and with the second command start the web server 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,703 subscribers to this blog!!!

Keep safe during the COVID-19 pandemic and help restart the world economy.

John

john.canessa@gmail.com

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.