Skip to content

Instantly share code, notes, and snippets.

@popdemtech
Last active January 10, 2023 07:35
Show Gist options
  • Select an option

  • Save popdemtech/2135374671079726f124bef19c735d42 to your computer and use it in GitHub Desktop.

Select an option

Save popdemtech/2135374671079726f124bef19c735d42 to your computer and use it in GitHub Desktop.
Build a Smartsite by PopularDemand
[
{
"title": "Front Matter",
"slug": "front-matter",
"content": "# `smartsite` by Popular Demand\n\n*Web Application Fundamentals Made Simple*\n\nHey everyone. Welcome to a guide to Web Development.\n\nThe purpose of `smartsite` is to provide interested individuals with\na launch point for building a personal web application.\n\nBefore beginning, the individual must be able to:\n<ol type=\"a\">\n <li>Download and install programs</li>\n <li>Browse the internet</li>\n <li>Use the command-line interface</li>\n</ol>\n\nUsing the command-line interface is likely the least familiar of the skills.\nLuckily, commands that are **necessary** for building the application\nwill be given by the walkthrough. Still, because the command-line will be one of the primary development tools, beginner level proficiency is recommended.\n\nThe guides will present a path of Node.js web development that it known to work.\nDifferences in operating system and setup may require modifications to the instructions.\n\n## The Process\nThere is One Step:\n1. Follow the Walkthrough",
"sectionType": "part",
"sequence": 0,
"parentSectionId": null,
"childSections": [
{
"title": "Introduction",
"slug": "introduction",
"content": "<h2 id=\"howtoreadthewalkthrough\">How to Follow the Walkthrough</h2><p>The Walkthrough is presented in three sections:</p><ul><li>Setup</li><li>Basics</li><li>Features</li></ul><h3 id=\"setup\">Setup</h3><p>The Setup section ensures the developer has the required librariesand programs installed. Anyone who has been developing web applicationsfor any amount of time may already have libraries like Node.js and PostgreSQL installed.Regardless, go through the setup steps to ensure parity with the example application.</p><h3 id=\"thebasics\">The Basics</h3><p>The Basics covers the key building blocks for creating a web application.</p><ul><li>Deliver static HTML pages</li><li>Deliver dynamically rendered content</li><li>Provide interactive front-end content</li><li>Connect to a database</li><li>Save user information in the database</li></ul><p>After completing the Basics, the developer will have completed building afull-fledged Web 2.0 application. With curiosity and initiative, any type of webapplication can be built from the skills and tools outlined in the Basics.</p><h3 id=\"features\">Features</h3><p>The third section, Features, is an exploration of the range of applicationscan be built from the tooling of the Basics. Features contains walkthroughsfor extending the functionality of smartsite.</p><p>The Features are standalone front-end or full-stack modules that can be completed in any order.</p><h2 id=\"howtobegin\">How to Begin</h2><p>Begin with the <strong>Setup</strong> instructions -- install the libraries and run the Node installation commands.</p><p>As you continue through the tutorial, what began as a directory with not much more than a walkthrough blossoms into a featured and interactive web application. This application can serve as your personal site, small-business database system, or become a product prototype.</p><h2 id=\"whatnext\">What Next</h2><p>After you've comepleted building out <code>smartsite</code>, you will have the tools of web development in hand. With the skills, you can:</p><ul><li>Provide value on the internet with your app ideas</li><li>Continue learning web development in pursuit of a career in software</li><li>Prototype the next Big Idea</li></ul><p>If you find yourself building out cool new features in your <code>smartsite</code>, aid future learners by contributing a walkthrough to the <code>smartsite</code> parent repository. Contributions will be added to the <strong>Features</strong> section as an optional <code>smartsite</code> add-on. Thanks in advance!</p><p>Cheers and Happy Building.<br> -The Author</p>",
"sectionType": "chapter",
"sequence": 1,
"parentSection": {
"parentSectionPart": 0,
"parentSectionType": "part",
"parentSectionSequence": 0
}
}
]
},
{
"title": "Setup",
"slug": "setup",
"content": "## README.md\nThis section ensures the you have the required libraries and programs installed.\n\nWe will be installing:\n* Git\n* The Node ecosystem: node.js, `npm` \n* `nvm` (MacOS only)\n\nIf you have been developing in JavaScript for a while, you may already have these programs installed. Still, read through the setup steps to ensure your system is in parity with the example setup.",
"sectionType": "part",
"sequence": 1,
"parentSectionId": null,
"childSections": [
{
"title": "Git My-app",
"slug": "git-my-app",
"content": "## Git `smartsite`\nGit the only developer tool required before beginning this walkthrough. It's required for you to run the first command, `git clone`, and sync the starting directory to your local computer.\n\n### What is Git?\n\nImagine every time you made a change to a project, you had to make a brand new directory, copy your existing work into it, make changes in this totally new directory, then _somehow_ totally replace the old work with the new work both locally and in production when the changes are finished. In the early days of the internet, this was solved with creative use of file transport protocol (FTP). The Git program both solves the tedious workflow and improves on full-file, FTP syncing.\n\nGit is a version control management system that maintains what the software looked like in the past and what it looks like now. It keeps track of each incremental change to the codebase which allows for efficient filesystem syncing between local and remote environments by only applying the changeset at the time of sync.\n\nA collection of files that is being tracked by Git is called a repository. The files on hard disk are called the Working Directory.\n\nA repository is a virtual collection, the working directory is the filesystem a developer is modifying. As changes in the working directory are accepted to be code complete, they are added via command to the Git repository. The repository can then be synced between local environments and remote environments.\n\nThe Git development workflow also provides the ability to split-off (or \"branch\") new development work from a pre-established \"master\" branch. This allows the developer to build new features on top of the currently sanctified codebase, only adding the a new changeset. Once the new changes are approved, finalizing the changes into the existing project takes one command. If the new changes end up not being desirable, they can be reverted, discarded and/or saved for a later date.\n\nIn essence, Git takes snapshots of application files over time and allows developers to manage each snapshot independent of one another. There may be a revision on the developer's local computer, a different revision being visited by live traffic in production, and an entirely different revision up for development team review on a shared repository management system like Github.\n\n### 1. Install Git\n\nTo get Git, use the [official installation guide](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) (https://git-scm.com/book/en/v2/Getting-Started-Installing-Git).\n\n\nYou're done with Git installation when you can run `git --version` and be presented with your installed Git gersion.\n\n<div class=\"filename\">command line</div>\n\n```\n$ git --version\n```\n\n### 2. Clone `smartsite`\nThe `git clone` command downloads a git repository from the internet to a local working directory. Within the terminal, navigate to the directory you'd like to save `smartsite` and run `git clone`.\n\n<div class=\"filename\">command line</div>\n\n```\n$ cd path/to/parent_directory\n$ git clone https://github.com/popdemtech/smartsite.git\n$ cd smartsite\n```\n\nThis series of commands downloads `smartsite` and places the terminal in the `smartsite` directory.\n\nThe starting `smartsite` directory is just this walkthrough. Following the steps provided, you will build out a Node.js web server, and the nearly empty directory will blossom into an internet application of whatever you wish it to be.\n\nWe just gotta create the application now. Let's get to it.\n\n### Resources\nA Simple Guide to Git: [http://rogerdudler.github.io/git-guide/](http://rogerdudler.github.io/git-guide/)",
"sectionType": "chapter",
"sequence": 1,
"parentSection": {
"parentSectionPart": 1,
"parentSectionType": "part",
"parentSectionSequence": 1
}
},
{
"title": "Install Nodejs",
"slug": "install-nodejs",
"content": "## Install Node.js\n\nThis walkthrough will cover building a web server application using Node.js. While JavaScript was initially developed for and still maintains its scripting dominance in a browser environment, Node.js is a JS runtime for use outside of a browser.\n\nNode.js is an open source development platform for executing JavaScript code server-side. While JavaScript runs natively in a browser (i.e. client-side), Node.js provides developers the platform with which to build applications for a controlled environment that runs on a host computer (server-side), separate from the JavaScript that is delivered to the client's browser. In this way, a Node.js application is comparable to PHP, Java, and Ruby, and other application environments that handle web-traffic requests, but are not delivered to the client.\n\nWindows users see [Install Node.js on Windows].\n\nMacOS users, check out [Install Node.js on MacOS] for installation instructions.\n\nLinux users, search a trusted search engine with the phrase \"install node js linux\" to find what you need.",
"sectionType": "chapter",
"sequence": 2,
"parentSection": {
"parentSectionPart": 1,
"parentSectionType": "part",
"parentSectionSequence": 1
}
},
{
"title": "Install Nodejs Windows",
"slug": "install-nodejs-windows",
"content": "## Install Node.js on Windows\n\n### 1. Navigate to nodejs.org\n\n<p style=\"text-align:center\">\n <img src=\"/assets/img/posts/install-nodejs-windows/walkthrough_0.png\" style=\"width:50%;min-width:320px;\" />\n</p>\n\nNavigate to [nodejs.org](https://nodejs.org) and select the version of node you want to download. Even number versions have Long-Term Support (LTS).\n\nLong-term support \"typically guarantees that critical bugs will be fixed for a total of 30 months.\" Production applications should use LTS versions. Use a more recent odd number version to test out latest features.\n\nRead more about node's release schedule in the Resources.\n\n### 2. Setup Wizard\n\n<p style=\"text-align:center\">\n <img src=\"/assets/img/posts/install-nodejs-windows/walkthrough_1.png\" style=\"width:50%;min-width:320px;\" />\n</p>\n\nOnce the installer finishes downloading, open the downloaded file to open the installation wizard.\n\n### 3. Accept Terms\n\n<p style=\"text-align:center\">\n <img src=\"/assets/img/posts/install-nodejs-windows/walkthrough_2.png\" style=\"width:50%;min-width:320px;\" />\n</p>\n\nAccept the terms of the License Agreement if you agree.\n\n### 4. Select the Installation Directory\n\n<p style=\"text-align:center\">\n <img src=\"/assets/img/posts/install-nodejs-windows/walkthrough_3.png\" style=\"width:50%;min-width:320px;\" />\n</p>\n\nThe default location, `C:\\Program Files\\nodejs\\`, is fine. If you install in a non-default location, ensure the directory is located within your command-line's `$PATH` variable.\n\n### 5. Customize Features\n\n<p style=\"text-align:center\">\n <img src=\"/assets/img/posts/install-nodejs-windows/walkthrough_4.png\" style=\"width:50%;min-width:320px;\" />\n</p>\n\nClick next unless you are certain you want something different. I have never customized this step.\n\n### 6. Install Tools for Native Modules\n\nYou will need a few software tools to be installed in addition to NodeJS in order to compile certain JavaScript/C++ npm modules. NPM modules are 3rd party libraries that can be used to extend the functionality of your application.\n\nIf you decide not to install the tools, they can be installed later.\n\n<p style=\"text-align:center\">\n <img src=\"/assets/img/posts/install-nodejs-windows/walkthrough_5.png\" style=\"width:50%;min-width:320px;\" />\n</p>\n\nI checked the box because I know I want the tools.\n\n### 7. Install\n\n<p style=\"text-align:center\">\n <img src=\"/assets/img/posts/install-nodejs-windows/walkthrough_6.png\" style=\"width:50%;min-width:320px;\" />\n</p>\n\nInstall.\n\n### 8. Watch the Progress Bar\n\n<p style=\"text-align:center\">\n <img src=\"/assets/img/posts/install-nodejs-windows/walkthrough_7.png\" style=\"width:50%;min-width:320px;\" />\n</p>\n\n<p style=\"text-align:center\">\n <img src=\"/assets/img/posts/install-nodejs-windows/installing_node_js.gif\" style=\"width:50%;min-width:320px;\" />\n</p>\n\nThe installation took me \\~3 minutes total.\n\n### 9. Allow Node.js to make changes to the device\n\n<p style=\"text-align:center\">\n <img src=\"/assets/img/posts/install-nodejs-windows/walkthrough_8.png\" style=\"width:50%;min-width:320px;\" />\n</p>\n\n### 10. After installation, Install Native Module Tools\n\n<p style=\"text-align:center\">\n <img src=\"/assets/img/posts/install-nodejs-windows/walkthrough_9.png\" style=\"width:50%;min-width:320px;\" />\n</p>\n\nIf you selected \"Automatically install the necessary tools\" in **Step 6**, a window will appear with some information about the libraries that are about to be installed. Continue through the prompts.\n\n### 11. Finish Tools' Install in Powershell \n\n<p style=\"text-align:center\">\n <img src=\"/assets/img/posts/install-nodejs-windows/walkthrough_10.png\" style=\"width:50%;min-width:320px;\" />\n</p>\n\nThe process will open a Powershell window with Administrator rights, and finish the installation in Powershell. Allow Powershell to make changes to the device.\n\n### 12. Wait for and Debug Tools Install\n\nThis installation process takes longer than the Node.js install. The installer recommends closing *all* programs other than the installer during the install process.\n\nI did not do that, and did not find the performance of my PC affected during install. However, I did experience an installation failure the first time...\n\n### 12a. Repair Native Modules Install\n\nIt is common for the native modules installation to \"fail\" the first time. It's so common the installation wizard comes with a Repair button. If the native modules installation fails the first time, reactivate the Node.js installer download, and select the 'Repair' option. See \"Repair Node Installation\" in the Resources for details. See the Resourcse as well if you have an installation failure not fixed by this solution:\n\nFind the downloaded file from **Step 1**, and select it.\n\n<p style=\"text-align:center\">\n <img src=\"/assets/img/posts/install-nodejs-windows/walkthrough_11.png\" style=\"width:50%;min-width:320px;\" />\n</p>\n\nSelect next, then select 'Repair.'\n\n<p style=\"text-align:center\">\n <img src=\"/assets/img/posts/install-nodejs-windows/walkthrough_12.png\" style=\"width:50%;min-width:320px;\" />\n</p>\n\n<p style=\"text-align:center\">\n <img src=\"/assets/img/posts/install-nodejs-windows/walkthrough_13.png\" style=\"width:50%;min-width:320px;\" />\n</p>\n\n<p style=\"text-align:center\">\n <img src=\"/assets/img/posts/install-nodejs-windows/walkthrough_14.png\" style=\"width:50%;min-width:320px;\" />\n</p>\n\n<p style=\"text-align:center\">\n <img src=\"/assets/img/posts/install-nodejs-windows/walkthrough_15.png\" style=\"width:50%;min-width:320px;\" />\n</p>\n\n### 13. Check Installation\n\nNode and NPM should now be installed. If you installed the native modules, you will have those as well. From Windows Terminal (or similar), run the following commands and check the output:\n\n<div class=\"filename\">command line</div>\n\n```\n> node -v\nv16.13.2\n\n> npm -v\nv8.2.1\n```\n\n`node -v` checks the version of node, and `npm -v` checks the version of npm, node package manager.\n\nIf native modules were installed, run the following to check the version of the installed libraries:\n\n<div class=\"filename\">command line</div>\n\n```\n> choco list -lo\n\nChocolatey v0.10.15\nchocolatey 0.10.15\n...long list...\nvisualstudio-installer 2.0.2\nvisualstudio2019-workload-vctools 1.0.1\nvisualstudio2019buildtools 16.11.9.0\n21 packages installed.\n```\n\nIn particular, check for the existance of visualstudio-installer, visualstudio2019-workload-vctools, and visualstudio2019buildtools libraries. (Note: The exact version and name of the Visual Studio tool may have progressed to higher versions than in the example output.)\n\nNow that Node is installed, we'll get to building out `smartsite`. Keep the following in mind: Node.js is useful for more than serving web requests. Node.js can be used to build desktop applications, command-line scripts, developer libraries (things that can be `npm install`ed), and more. The Node.js ecosystem is ripe for software creation.\n\n### Resources\n\nNode.js Release Schedule and Information: [nodejs.org/en/about/releases](https://nodejs.org/en/about/releases/)\n\nManage PATH System Variable on Windows: [docs.oracle.com](https://docs.oracle.com/en/database/oracle/machine-learning/oml4r/1.5.1/oread/creating-and-modifying-environment-variables-on-windows.html#GUID-DD6F9982-60D5-48F6-8270-A27EC53807D0)\n\nInstall Tools for Native Modules: [github.com/nodejs/node-gyp#on-windows](https://github.com/nodejs/node-gyp#on-windows)\n\nRepair Node Installation: [stackoverflow.com/a/68912225](https://stackoverflow.com/a/68912225)",
"sectionType": "section",
"sequence": 1,
"parentSection": {
"parentSectionPart": 1,
"parentSectionType": "chapter",
"parentSectionSequence": 2
}
},
{
"title": "Install Nodejs Mac",
"slug": "install-nodejs-mac",
"content": "## Install Node.js on MacOS\n\nTo develop a Node.js application on MacOS, the Node binaries must be installed. This guide walks through installing Homebrew, Node.js, and Node Version Manager (nvm).\n\n### 1. Create a user with admin access.\nChances are you are already a user with admin access. If you are aware that you are not a user with admin access, follow [these steps](https://osxdaily.com/2017/07/17/how-create-new-admin-account-mac/) (osxdaily.com) to create such a user. You will need a user *with* admin access to create this new user, so contact an admin if necessary.\n\n### 2. Install Homebrew\nHomebrew is a package manager for MacOS. An OS package manager is used for downloading programs and libraries. Homebrew is used for installing and managing versions of CLI tools and other packages.\n\n<div class=\"filename\">command line</div>\n\n```\n$ ruby -e \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)\"\n$ brew -v\n```\n\n### 3. Remove existing node versions\nIn case there is already a Node installation on the Mac, remove it. We will be using nvm to manage Node versions, and a pre-existing installation will hijack any invocations of the `node` executable.\n\n<div class=\"filename\">command line</div>\n\n```\n$ brew uninstall --force node\n```\n\n### 4. Install NVM\nNode Version Manager allows the developer to install and manage different versions of Node both a global and project-by-project basis.\n\n<div class=\"filename\">command line</div>\n\n```\n$ brew update\n$ brew install nvm\n```\n\n### 5. Follow the instructions output by the nvm installer\n<div class=\"filename\">command line output</div>\n\n```\nYou should create NVM's working directory if it doesn't exist:\n\n mkdir ~/.nvm\n\nAdd the following to ~/.zshrc or your desired shell\nconfiguration file:\n\n export NVM_DIR=\"$HOME/.nvm\"\n [ -s \"/usr/local/opt/nvm/nvm.sh\" ] && \\. \"/usr/local/opt/nvm/nvm.sh\" # This loads nvm\n [ -s \"/usr/local/opt/nvm/etc/bash_completion.d/nvm\" ] && \\. \"/usr/local/opt/nvm/etc/bash_completion.d/nvm\" # This loads nvm bash_completion\n\nYou can set $NVM_DIR to any location, but leaving it unchanged from\n/usr/local/opt/nvm will destroy any nvm-installed Node installations\nupon upgrade/reinstall.\n\nType `nvm help` for further information.\n```\n\nPractically, issue the following command:\n\n<div class=\"filename\">command line</div>\n\n```\n$ mkdir ~/.nvm\n```\n\nCopy the indicated output and paste it into `.zshrc`, and use the command `source` to load the new configuration into the active terminal.\n\n<div class=\"filename\">command line</div>\n\n```\n$ nano ~/.zshrc\n$ source ~/.zshrc\n```\n\n### 6. Install the latest long-term support version of Node.js.\n<div class=\"filename\">command line</div>\n\n```\n$ nvm install --lts\n$ nvm current\n```\n\n`nvm current` displays the currently active node version. It should be the version that was installed with `nvm install --lts`.\n\n### 7. Check the installations\nYou should now have nvm and Node.js installed. Check the installation. Here are the commands with example output.\n\n<div class=\"filename\">command line</div>\n\n```\n$ nvm -v\n0.39.1\n$ node -v\nv16.14.2\n```\n\nNow that Node is installed, we'll get to building out `smartsite`. Keep the following in mind: Node.js is useful for more than serving web requests. Node.js can be used to build desktop applications, command-line scripts, developer libraries (things that can be `npm install`ed), and more. The Node.js ecosystem is ripe for software creation.\n\n## Resources\n\nSimilar walkthrough: https://tecadmin.net/install-nvm-macos-with-homebrew/",
"sectionType": "section",
"sequence": 2,
"parentSection": {
"parentSectionPart": 1,
"parentSectionType": "chapter",
"parentSectionSequence": 2
}
},
{
"title": "Git Usage",
"slug": "git-usage",
"content": "## Git Usage\nGit is the industry-leading version control management tool. It provides character by character change tracking and syncing of changes between local and shared environment. Git commands and algorithms warrant a deep dive of their own. This walkthrough provides the simplest possible `git` workflow for a solo developer.\n\nA useful advantage are the branching and merge strategies provided by Git which allow for multiple developers to work within the same codebase while keeping in sync with other developers' changes. For a solo developer, these strategies are useful in organizing product development and capturing each incremental change in a visualizable format.\n\nGit is also required for Heroku, the deployed environment used in this walkthough. This will keep the files we develop locally in sync with the public server's filesystem.\n\n### 1. Create .gitignore file\nA `.gitignore` file is used to define which files and folders should not be saved to version control. Common elements not saved to version control are in-project dependency folders, such as `node_modules`, files containing sensitive information (such as private keys), and certain files used only by the developer's local operating system, such as Apple's `.DS_Store` file.\n\nCreate a file named `.gitignore` in the root directory with the following:\n\n<div class=\"filename\">.gitignore</div>\n\n```\n/node_modules\nnpm-debug.log\n.DS_Store\n/*.env\n```\n\n### 2. Save changes with `git`\nAs changes are made in local development, Git keeps track of them, but does not automatically save the changes to version control. Saving to version control is a two step process. First the changes must be \"staged\". This is essentially a holding area for changes that the developer can review before finalizing the changes. The second step is to finalize, or \"commit\", the changes.\n\nThe command to stage changes is `git add`. The command to finalize the changes into version control is `git commit`.\n\n1. `git add`\nGit's `add` command takes a list of files and directories that should be staged as a parameter.\n\n<div class=\"filename\">command line</div>\n\n```\n$ git add .\n```\n\nThe `.` symbol is shorthand for \"the current working directory.\" Calling `git add` with this parameter signals to Git to save all changes in the current directory. The command can also be run with a list file and directory names as parameters -- e.g. `git add index.js package.json`.\n\n2. `git commit`\nGit enforces that every commit have a commit message describing why the commit was made. A repository's commit messages should be a human-readable log of the changes over time. Use the `-m` flag with `git commit` to add a commit message inline. If the `-m` flag is not used, the terminal will open the default text editor for the developer to enter the commit message.\n\n<div class=\"filename\">command line</div>\n\n```\n$ git commit -m 'Initialize my app'\n```\n\nGit provides an immense catalog of functionality for repository management. As a developer's needs grow more complex, an expanded Git repetoire is a must. I recommend is this [Simple Guide to Git](http://rogerdudler.github.io/git-guide/)(http://rogerdudler.github.io/git-guide/) for next steps in building Git proficiency.",
"sectionType": "chapter",
"sequence": 3,
"parentSection": {
"parentSectionPart": 1,
"parentSectionType": "part",
"parentSectionSequence": 1
}
},
{
"title": "Initialize Node",
"slug": "initialize-node",
"content": "## Initialize Node\nIt's time to create the first files of the web application. \n\n### Initialize\nThe `npm init` command will start a setup wizard for the Node application. In Terminal, navigate to the application directory, and run `npm init`.\n\n<div class=\"filename\">command line</div>\n\n```bash\n$ cd path/to/smartsite\n$ npm init\n```\n\nFor `entry point:`, use `index.js`; it is the default option, and required for parity with the walkthroughs. The default options for the rest of the selections is fine. Feel free to investigate the meaning of each, and customize the values as desired. Selection made in this wizard can be changed.\n\nHere is example output from `npm init`:\n\n<div class=\"filename\">command line</div>\n\n```\n$ npm init\nThis utility will walk you through creating a package.json file.\nIt only covers the most common items, and tries to guess sensible defaults.\n\nSee `npm help init` for definitive documentation on these fields\nand exactly what they do.\n\nUse `npm install <pkg>` afterwards to install a package and\nsave it as a dependency in the package.json file.\n\nPress ^C at any time to quit.\npackage name: (smartsite) \nversion: (1.0.0) \ndescription: smartsite Web Application Guide\nentry point: (index.js) \ntest command: \ngit repository: (https://github.com/popdemtech/smartsite.git) \nkeywords: \nauthor: Popular Demand\nlicense: (ISC) \nAbout to write to /Users/popdemtech/popdemtech/smartsite/package.json:\n\n{\n \"name\": \"smartsite\",\n \"version\": \"1.0.0\",\n \"description\": \"smartsite Web Application Guide\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/popdemtech/smartsite.git\"\n },\n \"author\": \"Popular Demand\",\n \"license\": \"ISC\",\n \"bugs\": {\n \"url\": \"https://github.com/popdemtech/smartsite/issues\"\n },\n \"homepage\": \"https://github.com/popdemtech/smartsite#readme\"\n}\n\n\nIs this OK? (yes)\n``` \n\nThis command generates a `package.json` file in the directory in which it is run. `package.json` is used for configuration of Node.js applications, and will be revisited throughout the development process.\n\n### Start the application\nAn application is a software script that is executed on a computer. To \"start\" this application, like every software, application code must be written and a command that starts the application must be defined.\n\nNode.js scripts are run by passing its filename to the `node` executable. The `node` program reads the file, interprets the Javascript, and runs the functionality specified by the application.\n\nTo run the application, first create a file, `index.js` for the application code.\n\n1. Create `index.js`\nIn the root of the project, create a file titled `index.js`. Any valid JavaScript can go in this file -- a `console.log` statement is shown in the example.\n\n<div class=\"filename\">index.js</div>\n\n```javascript\nconsole.log('Welcome to SmaRtsite!');\n```\n\nAt this point, the application can be run with `node index.js`.\n\n2. Create the start script\nA Node.js application's `package.json` is the place to define commonly used commands such as `start` and `test`. A top-level property `\"scripts\"` is used to map developer-selected command names to executable scripts. `package.json` already contains a `test` script.\n\nAdd a script called `start` that executes the `node` executable with `index.js`, and save the file.\n\n<div class=\"filename\">package.json</div>\n\n```js\n{\n ...,\n \"scripts\": {\n \"start\": \"node index.js\",\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n },\n ...\n}\n```\n\nA script defined in `\"scripts\"` can be invoked from the command line with `npm run [script]`.\n\n3. Run start script\n\n<div class=\"filename\">command line</div>\n\n```\n$ npm run start\n```\n\nYou should see the output `Welcome to SmaRtsite!` in the console. Just like that we have a simple yet functional Node.js application.",
"sectionType": "chapter",
"sequence": 4,
"parentSection": {
"parentSectionPart": 1,
"parentSectionType": "part",
"parentSectionSequence": 1
}
}
]
},
{
"title": "Basics",
"slug": "basics",
"content": "## README.md\n\nThis section covers implementing key building blocks for creating a web application.\n\n* The webserver\n* The view layer\n* User authentication\n* Databases\n* Environment variables\n* A deployed environment\n\nThe section concludes with building a full-stack feature using all of the covered components.",
"sectionType": "part",
"sequence": 2,
"parentSectionId": null,
"childSections": [
{
"title": "Create Webserver",
"slug": "create-webserver",
"content": "## Create the Web Server\nAs it stands, `smartsite` is a functioning Node.js application, but it doesn't do much. It prints a message in the terminal. We're here to build an application that serves web traffic. This means a user can navigate to our web pages and functionality from an internet browser.\n\nFor the purposes of this guide, the term \"web server\" should be taken to mean an application that serves web traffic. The term \"web server\" can (and often does) apply to any physical or digital component that makes up the OSI model. These components funtionally operate different levels of abstraction, and in the most general sense, constitute a pipeline of request handling. Node.js web servers operate at the \"Application Layer\" of the model.\n\nA web server library written for the JavaScript ecosystem will be included into application scripts. There are many such libraries in the Node.js ecosystem from which to choose. The ideal library for our purposes provides a developer friendly abstraction over the gritty details of TCP and HTTP communication protocols. A large network of developers using the library is a strong bonus as well. Express [https://expressjs.com](https://expressjs.com) is a web framework that checks all of the boxes.\n\n### Express\n\nExpress provides an abstraction over low-level HTTP handling by using sensible defaults for HTTP configuration, while still allowing for low-level configuation as the needs of the application are discovered. For the developer, Express provides a straight-forward, route declaration approach for serving web requests.\n\nExpress has been a mainstay library for since the early days of Node.js, and beginner to advanced online resources can be found with ease.\n\n### 1. Install Express\n\nUse `npm` to install Express. Within `smartsite`'s root directory, run the following:\n\n<div class=\"filename\">command line</div>\n\n```\n$ npm install express\n```\n\nThis command adds Express as a dependency to the application, and installs the library into the `node_modules` folder. Since Express is the first external library added to the application, the `node_modules` folder will be created in the root directory.\n\n### 2. Create the Express server\nExpress provides JavaScript classes and functions that, when used within a Node.js script, start a webserver process. To do so, we'll need to `require` the Express library, then instantiate an instance of Express.\n\nAn instance of Express is conventionally called `app`. The instance provides methods for routing HTTP requests, rendering HTML views, registering a template engine, and configuring middleware. `smartsite` will utilize all of these methods.\n\nFirst, modify `index.js` to import in the `express` library and instantiate an `app`. Register a `GET` route to the root route, `/`.\n\n<div class=\"filename\">index.js</div>\n\n```javascript\nconst express = require('express');\nconst app = express();\nconst PORT = 3000;\n\napp.get('/', function(request, response) {\n response.send('<h1>Welcome to SmaRtsite!</h1>');\n});\n\napp.listen(PORT, () => {\n console.log(`SmaRtsite listening on port ${PORT}`);\n});\n```\n\n### 3. Run the Application\nStarting the application with `npm run start` will begin the Express server. Based on the code above, this web server can be accessed by navigating to `http://localhost:3000` in a web browser.\n\nOpen a web browser and navigate to `localhost:3000`. You should see a large heading with the words \"Welcome to SmaRtsite!\".\n\n[image welcome to my app]\n\n### 6. Git commit the changes\nThis was a significant unit of development. The Express library was added and the initial web route was added to the application. `git commit` the changes to signify the completion of this development.\n\n<div class=\"filename\">command line</div>\n\n```\n$ git add .\n$ git commit -m 'Add express'\n```\n\n### Resources\nExpress: [expressjs.com](https://expressjs.com)",
"sectionType": "chapter",
"sequence": 1,
"parentSection": {
"parentSectionPart": 2,
"parentSectionType": "part",
"parentSectionSequence": 2
}
},
{
"title": "Filesystem Watcher",
"slug": "filesystem-watcher",
"content": "## Add a Filesystem Watcher\nA filesystem watcher is a program that ensures a running development application is providing the most recent changes to code. The use case for a filesystem watcher is best illustrated by example. \n\n1. Ensure the node server is running `npm run start`\n\n2. Modify a `index.js`\nThe current state of the root route in `index.js` is that it returns an HTML string with the phrase `Hello World`.\n\n<div class=\"filename\">index.js</div>\n\n```javascript\napp.get('/', function(request, response) {\n response.send('<h1>Welcome to SmaRtsite!</h1>');\n});\n```\n\nChange the sent response to read `Hello World` instead of `Welcome to SmaRtsite`, and save the file.\n\n<div class=\"filename\">index.js</div>\n\n```javascript\napp.get('/', function(request, response) {\n response.send('<h1>Hello World!</h1>');\n});\n```\n\n3. Check for changes\nIn the broswer, navigate to `localhost:3000`, and check out the heading. It still says `Hello World!`.\n\nThis is because when the application is run with `node index.js`, all application files are cached in the state they were in when the command was invoked. To see the modified response, stop the currently running server with `CMD+C` or `CTRL+C` depending on your operating system, and restart it with `npm run start`. Navigating to the browser now will display the updated text.\n\nRestarting the server after every change is tedious and will seem *more* tedious over time. And presents a near impossible developer experience long-term. As such, `smartsite` will implement the file watching library `nodemon`.\n\n### 1. Add nodemon\nNodemon is a library that is used to initialize a process from the local operating system, and is therefore a development dependency rather than an application dependency. Install the library as a dev dependency.\n\n1. Install nodemon\n<div class=\"filename\">command line</div>\n\n```bash\n$ npm install --save-dev nodemon\n```\n\n2. Use nodemon\nnodemon wraps the `node` process with an additional file-checking functionality. Change the start script within `package.json` to use `nodemon` instead of `node` to start the server process.\n\n<div class=\"filename\">package.json</div>\n\n```javascript\n{\n ...,\n \"scripts\": {\n \"start\": \"npx nodemon .\",\n ...\n },\n ...\n}\n```\n\nThe `npx` command can be thought of as \"**n**ode **p**ackage e**x**ecute.\" This signals to use the executable provided by a program found within the local `node_modules` directory -- in this case `nodemon`.\n\n3. Restart the server\nRunning `npm run start` will now invoke `nodemon`. `nodemon` will start the application as usual and restart the server process as code within the directory is changed. Restart the server.\n\n<div class=\"filename\">command line</div>\n\n```\n$ npm run start\n```\n\n4. Modify the code\nChange the text sent from the root route back to `Welcome to SmaRtsite`, and *do not* restart the server.\n\n<div class=\"filename\">index.js</div>\n\n```javascript\napp.get('/', function(request, response) {\n response.send('<h1>Welcome to SmaRtsite!</h1>');\n});\n```\n\n5. Check for changes\nIn the broswer, navigate to `localhost:3000`, and check out the heading. It now says `Welcome to SmaRtsite!` without needing a server restart. The filesystem watcher is working!\n\n### 6. Git commit the changes\nThis was a significant unit of development. A development library was added, and it's functionality was fully implemented. `git commit` the changes to signify the completion of this development.\n\n<div class=\"filename\">command line</div>\n\n```\n$ git add .\n$ git commit -m 'Add nodemon'\n```\n\n### Resources\nNodemon: [npmjs.com/package/nodemon](https://www.npmjs.com/package/nodemon)",
"sectionType": "section",
"sequence": 1,
"parentSection": {
"parentSectionPart": 2,
"parentSectionType": "chapter",
"parentSectionSequence": 1
}
},
{
"title": "Sending Web Pages",
"slug": "sending-web-pages",
"content": "## Sending Web Pages\nCurrently, the application is configured to send an HTML string when the root route, `/`, is requested. While this is a valid use of Express route handling, a better approach is to keep end-user presentation and the application logic into separate files.\n\n<dl>\n <dt>Application Logic</dt>\n <dd>The scripted code that handles the request/response cycle</dd>\n <dt>Presentation</dt>\n <dd>The response displayed to the consumer</dd>\n</dl>\n\nThe separation of concerns between the routing and view layers is standard practice. This technique allows developer to optimize and organize the two areas separately which will become more important as the application grows.\n\n### The Presentation Layer\nIt is safe to expect web-traffic to be viewed using a internet browsing application such as Chrome or Microsoft Edge. Modern internet broswers are equipped to translate many common web response formats into human usable form. The most familiar of these formats is likely HTML.\n\nTo start `smartsite`'s presentation layer, we will send a basic HTML file.\n\n1. Create a file named `index.html` in the root directory.\n\n<div class=\"filename\">index.html</div>\n\n```html\n<!DOCTYPE html>\n<head>\n <title>SmaRtsite</title>\n</head>\n<html>\n <body>\n <h1>Welcome to SmaRtsite!</h1>\n </body>\n</html>\n```\n\n2. Send the `index.html` as the response from `/`.\nModify the route handler of `/` to use `sendFile` instead of `send`.\n\n<div class=\"filename\">index.js</div>\n\n```javascript\napp.get('/', function(request, response) {\n response.sendFile(__dirname + '/index.html');\n});\n```\n\n`__dirname` is a Node.js variable containing the directory name of the currently executing file. Because we know the location of `index.html` to be in the same directory as `index.js`, simply appending the HTML's filename to `__dirname` yields the correct location for the file.\n\n### Resources\nSeparation of Concerns: [https://deviq.com/principles/separation-of-concerns](https://deviq.com/principles/separation-of-concerns)\n\nWhat is HTML?: [https://www.hostinger.com/tutorials/what-is-html](https://www.hostinger.com/tutorials/what-is-html)",
"sectionType": "chapter",
"sequence": 2,
"parentSection": {
"parentSectionPart": 2,
"parentSectionType": "part",
"parentSectionSequence": 2
}
},
{
"title": "View Templates",
"slug": "view-templates",
"content": "## View Templates\n\nStatic HTML files make up the majority of web pages delivered on the internet. An HTML file is considered \"static\" because the content is the same regardless of user identity or any other real-time factors. The benefit of a web *application* is that the application layer has the ability to process the user request and deliver a dynamic experience.\n\nThe ability to deliver a dynamic experience at the view layer is accomplished by use of **view templating.** This allows developers to create files that are a mix of static content and variables which are determined at the time the template is rendered, e.g. in response to a user request.\n\nA template file is written in a **template language**. Template languages are often HTML-like, and support variable injection and often more complex scripting logic such as `for` loops. Modern web browsers cannot natively read the template language. A **templating engine** handles the conversion of a template and variables to an HTML file which is then delivered to the users' browsers.\n\nA view template may have the content `<p>Hello, {{ user.name }}!</p>`, and a template engine would render `<p>Hello, Alexa!</p>`.\n\nThere are several template languages from which to choose. Because of the separation of presentational concerns from application logic, a given language is usually not specfic to a given application architecture, e.g. Node.js. Learning the template language once is transferrable The application logic will invoke the template *engine* with parameters for which template file to render and what variables should be rendered therein, so it is important to choose a language with a respective template engine compatible with the application.\n\nGiven the ubiquity of view templating across all web server architectures, the problem of compatibility is not generally a concern. Many architectures come with built-in template rendering and a default templating language, and a developer can customize away from the default by adding a new rendering library.\n\n### Resources\n\nTemplate Engines: [https://expressjs.com/en/guide/using-template-engines.html](https://expressjs.com/en/guide/using-template-engines.html)",
"sectionType": "section",
"sequence": 1,
"parentSection": {
"parentSectionPart": 2,
"parentSectionType": "chapter",
"parentSectionSequence": 2
}
},
{
"title": "Add Template Engine",
"slug": "add-template-engine",
"content": "## Add a Template Engine\nThe Express ecosystem supports many trusted template languages. `smartsite` will use the Liquid template language.\n\n### 1. Install template libraries\nThe Node.js application requires functionality for parsing and rendering the template language into HTML. The package `liquidjs` provides the JavaScript bindings for Liquid template rendering. use `npm install` to add the library.\n\n<div class=\"filename\">command line</div>\n\n```\n$ npm install liquidjs\n```\n\n### 2. Register the Liquid template engine\nUsing `liquidjs` as the template engine in the application server requires importing the library, initializing it, and registering it with the Express `app`.\n\nCode like the following should be added to `index.js` before the route declarations.\n\n<div class=\"filename\">index.js</div>\n\n```javascript\nconst { Liquid } = require('liquidjs');\napp.engine('liquid', new Liquid().express());\n```\n\nExpress' `app.engine` method relates a file extension with a rendering engine. As Liquid files are created, they should be created with the `.liquid` file extension (e.g. `filename.liquid`). Express allows multiple `app.engine`s to be set. As such, multiple extensions and multiple rendering engines are valid.\n\n### 3. Create a view template\nA liquid template file supports all valid HTML with the additional functionality of variable rendering and logicial operators. They use a file extension of `.liquid` instead of `.html`. Copy the current `index.html` into a new file named `index.liquid` in the root directory of the application.\n\n<div class=\"filename\">index.liquid</div>\n\n```html\n<!DOCTYPE html>\n<head>\n <title>SmaRtsite</title>\n</head>\n<html>\n <body>\n <h1>Welcome to SmaRtsite!</h1>\n </body>\n</html>\n```\n\n### 4. Render the view template\nTo send a static HTML file, Express' `response.sendFile` method was used. In the case of view template rendering, a different method must be used to indicate the desired response to send to the user must first be generated through a templating engine, `response.render`.\n\n`response.render` accepts two parameters, 1) the filename and 2) an object of variables with which to render the file.\n\n1. Use `response.render` within the root route handler in place of `response.sendFile`.\n\n<div class=\"filename\">index.js</div>\n\n```javascript\napp.get('/', function(request, response) {\n response.render(__dirname + '/index.liquid');\n});\n```\n\n2. Save and navigate to `localhost:3000`.\n\n[image welcome to my app]\n\n\n### 5. Configure view options\nAn Express application can be configured with a variety of view options. Setting these options globally well explicity set filesystem architecture, and often allows for cleaner code in Express middleware and improves developer experience.\n\nTo continue the trend of clearly separating concerns, the architecture of `smartsite` will utilize a views directory. Once created, files that are meant to be rendered and/or sent as a user response should be placed in this folder to clearly separate the presentation layer from the JavaScript logic.\n\n1. Create a new folder named `views` in the root directory\nExpress, by default, will look for views (i.e. templates) in a directory named `views`. Specifically, it will look for a directory matching the definition `process.cwd() + '/views'`, where `process.cwd()` is the \"current working directory\" (`cwd`). As most node applications initialize from the root directory, the expanded file path is `<root directory>/views`. Although `smartsite` will utilize the default, this setting is configurable.\n\n2. Move `index.liquid` into the `views` folder.\n\n<div class=\"filename\">views/index.liquid</div>\n\n```html\n<!DOCTYPE html>\n<head>\n <title>SmaRtsite</title>\n</head>\n<html>\n <body>\n <h1>Welcome to SmaRtsite!</h1>\n </body>\n</html>\n```\n\nAt this point, visiting `localhost:3000` should lead to an error response returned. follow the next step to correct this.\n\n3. Modify `index.js` to render `views/index.liquid`\nWith the Express application set to default its search for views in `/views`, route handlers no longer have to specify the full file path to renderable files. Modify the handler of `/` by removing references to `_dirname`.\n\n<div class=\"filename\">index.js</div>\n\n```javascript\napp.get('/', function(request, response) {\n response.render('index.liquid');\n});\n```\n\n`localhost:3000` should now render the familiar \"Welcome!\" message.\n\n4. Set the view engine\nExpress' `app.set` method provides a way for developers to configure application-wide settings. A standard setting is \"view engine\" which is used to configure the default file extension for views. Adding the view engine setting allows for developers to omit the file extension from calls to `app.render`.\n\nAdd the setting to `index.js` and remove the `.liquid` extension in the root route.\n\n```javascript\napp.set('view engine', 'liquid');\n\napp.get('/', function(request, response) {\n response.render('index');\n});\n```\n\nThe application should work as expected.\n\n### 6. `git commit`\nConfiguring and initializing the view engine within `smartsite` is a significant unit of development. This is a perfect time to bookmark filesystem state within version control.\n\n<div class=\"filename\">command line</div>\n\n```\n$ git status\n$ git add .\n$ git commit -m 'Add template engine'\n```\n\n**Keep in mind:** As application development continues, place template files in the `/views` directory with a file extension of `.liquid`. The files may contain static or dynamic content.\n\n### Resources\nExpress' `app.set`: [https://expressjs.com/en/api.html](https://expressjs.com/en/api.html#app.set)\n\nExpress' `response.render`: [https://expressjs.com/en/api.html](https://expressjs.com/en/api.html#res.render)",
"sectionType": "section",
"sequence": 2,
"parentSection": {
"parentSectionPart": 2,
"parentSectionType": "chapter",
"parentSectionSequence": 2
}
},
{
"title": "Dynamic Content",
"slug": "dynamic-content",
"content": "## Dynamically Render Content\n\nA benefit of using view templates is the ability to add content to be rendered at the point of a user request. Within a Liquid template, use the syntax `{{ variable_name }}` to indicate a value passed at render time should be rendered to string. In addition, Liquid's `if/else` syntax `{% if variable_name %} ... {% else %} ... {% endif %}`, offers basic logical switching to determine what block of content to render based on whether the variable is defined at render time. If the statement evaluates to true, \n\nTo directly render a variable, use the double curly brace syntax, `{{ }}`. To script non-rendered view logic, use the curly brace and percent sign syntax, `{% %}`.\n\n### 1. Modify the view to respond to dynamically injected variables.\nAdd a section to `index.liquid` that renders passed in variables. \n\n<div class=\"filename\">views/index.liquid</div>\n\n```html\n<!DOCTYPE html>\n<head>\n <title>SmaRtsite</title>\n</head>\n<html>\n <body>\n <h1>Welcome to SmaRtsite!</h1>\n <p>This application is running in the <b>{{ nodeEnv }}</b> environment.</p>\n {% if debug %}\n <p><b>Debug Information</b></p>\n <p>Node version: {{ nodeVersion }}</p>\n <p>Server Time: {{ serverTime }}</p>\n {% endif %}\n <p></p>\n </body>\n</html>\n```\n\nNotice that the \"Debug Information\" section will only render if the `debug` variable is defined. If you navigate to the page at `localhost:3000`, there will be no visible difference. We need to render the page with at least the `debug` variable.\n\n### 2. Render the template with variables.\nModify the `/` route handler to define the variables `debug`, `nodeVersion`, and `serverTime`, and pass them in as the second parameter to `response.render`.\n\n<div class=\"filename\">index.js</div>\n\n```javascript\napp.get('/', function(request, response) {\n const debug = request.query.debug;\n const nodeVersion = process.version;\n const serverTime = new Date();\n response.render('index', { debug, nodeVersion, serverTime });\n});\n```\n\nNotice the definition of the `debug` variable: `request.query.debug`. The `query` property of Express `Request` objects returns the query parameters section of the requested URL string. By default, query parameters found at `request.query` are formatted as a JavaScript object. For example, the query string of `?debug=true&limit=10` will be of the form `{ debug: 'true', limit: '10' }`.\n\n**Note:** Query string values are necessarily coerced to string for HTTP transport. While a developer may intend to use a boolean or number value from the query string, Express provides the values as strings that must be type cast for use as a boolean or number, etc.\n\n### 3. Load the page\nWith the above changes in place, loading the page at `localhost:3000` renders the same landing page.\n\nTo see the new changes, request the page with a `debug` query string parameter: `localhost:3000?debug=true`. The page should now display a section of \"Debug Information.\"\n\n[image debug information]\n\n### 4. `git commit`\nDynamically rendering the index view within `smartsite` is a significant unit of development. This is a perfect time to save state to version control.\n\n<div class=\"filename\">command line</div>\n\n```\n$ git status\n$ git add .\n$ git commit -m 'Dynamically render index.liquid'\n```\n\n### Review\n\nDynamic display content must be located in the `/views` directory with a file extension of `.liquid`. The files may contain static or dynamic content. If the content should be dynamically rendered, provide an object of local variables as the second parameter `response.render` within the route handler.\n\nThe Liquid template language provides additional functiontality to the HTML specification in the form of flow control (`if/else`), iteration, and many more advanced operations. Visit the Liquid reference guide in **Resources** section to explore Liquid's full feature set.\n\n### Resources\nLiquid Template Language: [https://shopify.github.io/liquid/](https://shopify.github.io/liquid/)",
"sectionType": "section",
"sequence": 3,
"parentSection": {
"parentSectionPart": 2,
"parentSectionType": "chapter",
"parentSectionSequence": 2
}
},
{
"title": "Liquid Templating",
"slug": "liquid-templating",
"content": "## Liquid Templating\nA benefit of a template engine is the ability to separate units of presentation. This allows developers to define view logic once, and reuse the template across multiple pages. This concept will become more explicit by practice. This section details reusing template components between pages to eliminate the need to repeat and maintain separate view scripts.\n\n### 1. Create a new page\nWithin the `views` directory, create a new file names `hello-world.liquid`.\n\n<div class=\"filename\">views/hello-world.liquid</div>\n\n```html\n<!DOCTYPE html>\n<head>\n <title>SmaRtsite</title>\n</head>\n<html>\n <body>\n <h1>Hello World!</h1>\n\n {% if showDog %}\n <img src=\"https://i.imgur.com/5Swc751.png\" alt=\"brown dog marley\" width=\"150px\"/>\n {% endif %}\n\n <p>Show Marley with query parameter <code>showDog</code>.</p>\n <p><code>Page loaded at {{ serverDate }}.</code></p>\n </body>\n</html>\n```\n\n### 2. Create a new route\nRegister a route with the application to allow users to access the new page from a web browser.\n\n<div class=\"filename\">index.js</div>\n\n```javascript\napp.get('/hello-world', function(request, response) {\n const showDog = request.query.showDog;\n response.render('hello-world', {\n showDog,\n serverDate: new Date()\n });\n});\n```\n\nWith the server running (`npm run start`), navigate to `localhost:3000/hello-world`, and you should be presented with the greeting \"Hello World!\"\n\n### The Need for Templating\n\nTake a look at `index.liquid` and `hello-world.liquid`. Both pages have the standard HTML boilerplate -- `<html>`, `<head>`, `<body>` -- in common. In fact, any new HTML page will need the basic HTML layout. As front-end development development, it is highly likely that `CSS` and JavaScript assets will be shared between pages. Given the current paradigm of fully separating each web page, a developer adding a global CSS asset would have to add a `<link>` tag *each* .liquid file.\n\nStandard application of the DRY principle (Don't Repeat Yourself) dictates we isolate and define repeated patterns, and reference the one-time definition where necessary. To DRY up `smartsite`'s presentational layer, we need to isolate the base HTML markup layout, and implement the layout in each of the view scripts. This strategy is known as \"template inheritance.\"\n\n### 3. Add a Liquid `layout`\nLiquid has a standard concept of a \"layout\" template. A layout template defines whatever view logic it is meant to encapsulate, and defines areas within its markup meant to be customized by each implementer of the layout.\n\nCreate a file to serve as the layout. This file will contain HTML boilerplate, as well as a delimited section for child pages to provide custom content.\n\n<div class=\"filename\">views/default-layout.liquid</div>\n\n```html\n<!DOCTYPE html5>\n<html>\n <head>\n <title>smartsite</title>\n </head>\n <body>\n <header>\n <a href=\"/\">home</a>\n </header>\n {% block content %}\n default-html.liquid's default content is showing.\n {% endblock %}\n <br/><br/>\n <footer>&copy; by Popular Demand</footer>\n </body>\n</html>\n```\n\nThe syntax `{% block <block_name> %}{% endblock %}` specifies an area of the template to be overwritten by child pages. A developer can provide default markup within the layouts `block`; the default content is rendered if the child page does not define its own markup for the area.\n\n### 4. Use the layout\nThe `block` to be referenced in the sub-pages is named `content`. An individual sub-page must reference the parent layout, `default-layout.liquid`, and define its own `content` block. A reference to the parent layout is defined with a `layout` block.\n\nModify `hello-world.liquid` to implement the layout and define custom `content`.\n\n<div class=\"filename\">views/hello-world.liquid</div>\n\n```html\n{% layout 'default-layout.liquid' %}\n\n{% block content %}\n <h1>Hello World!</h1>\n\n {% if showDog %}\n <img src=\"https://i.imgur.com/5Swc751.png\" alt=\"brown dog marley\" width=\"150px\"/>\n {% endif %}\n\n <p>Show Marley with query parameter <code>showDog</code>.</p>\n <p><code>Page loaded at {{ serverDate }}.</code></p>\n{% endblock %}\n```\n\nNotice this page no longer features any boiler plate HTML. It only defines two features -- a `layout`\nblock and a `content` block.\n\nFrom the browser, reload the `/hello-world` route to be delivered the page with its newly defined layout. The main content of the pages, a heading and two paragraphs should remain visible. There should also be two elements added from the `default-layout.liquid` layout -- a page header with a link to the root route and a footer element with site information.\n\n### 5. Refactor `index.liquid`\n<dl>\n <dt>Refactor</dt>\n <dd>restructure the code of an application so as to improve it without altering functionality</dd>\n</dl>\n\nIsolating the base HTML into a layout was an improvement to the codebase in terms of maintainability. Let's reuse this architecture in all of the pages of the application.\n\n<div class=\"filename\">views/index.liquid</div>\n\n```html\n{% layout 'default-layout.liquid' %}\n\n{% block content %}\n <h1>Welcome to SmaRtsite!</h1>\n {% if debug %}\n <p><b>Debug Information</b></p>\n <p>Node version: {{ nodeVersion }}</p>\n <p>Server Time: {{ serverTime }}</p>\n {% endif %}\n{% endblock %}\n```\n\nAgain, the child page must define its parent `layout` and the `content` block to fill in the layout's `content` block.\n\nNavigating to the root route, `/`, in the broswer should render the `Welcome to SmaRtsite!` heading as well as the Header and footer defined in `default-layout.liquid`.\n\n### Review\n\n`block` is a standard tag provided by Liquid. A layout file uses a `block` tag to define an area that will be replaces with a child page's content. A child page uses `block` to define what to render in its layout's identically named `block`.\n\n`layout` is a functional tag provided by Liquid. It is used in a child page to define what layout the defined markup should be rendered within.\n\nIn the specific case of these changes, \"`content`\" is a developer defined variable name that references an area within the parent layout to be replaced by the markup of a identically named block within the child page.\n\n\n### 6. Add a link to Hello World!\nAs new pages are added to the application, it is good practice to give users an easy method of navigating to the page. Below the heading on the index page, add a link to `hello-world`. (**Note:** In this code block and going forward, only the affected parts of the code are shown. It's unnecessary to alter the unshown elements. Use discernment.)\n\n<div class=\"filename\">views/index.liquid</div>\n\n```html\n<h1>Welcome to SmaRtsite!</h1>\n<p>Links</p>\n<ul>\n <li><a href=\"/hello-world\">hello-world</a></li>\n</ul>\n```\n\n### 7. `git commit`\nCreating a view layout architecture within `smartsite` is a significant unit of development. Time to save to version control.\n\n<div class=\"filename\">command line</div>\n\n```\n$ git status\n$ git add .\n$ git commit -m 'Add default layout and hello-world'\n```\n\n### Reference\n1. Liquid's `layout`: [https://liquidjs.com/tags/layout.html](https://liquidjs.com/tags/layout.html)",
"sectionType": "section",
"sequence": 4,
"parentSection": {
"parentSectionPart": 2,
"parentSectionType": "chapter",
"parentSectionSequence": 2
}
},
{
"title": "Deployed Env Heroku",
"slug": "deployed-env-heroku",
"content": "## A Deployed Environment\n\nSo far, `smartsite` has only been served from a local development server. To open the application for public web traffic, the application has to have a public IP address, the proper configuration with OSI layer-7 programs allowing public web traffic. Chances are you do not want to open your personal computer to public traffic. As well, learning how to provision a operating system level server is a walkthrough in its own. Luckily, there are Platforms-as-a-Service that provide fully-provisioned server space for launching public web applications with ease. One such platform is Heroku.\n\n### Heroku\n\nHeroku provides the public server space `smartsite` needs. The Heroku platform offers server-processing in the form of what they call \"dynos.\" Heroku's free-tier includes unlimited dynos and 550 dyno hours per month. Verifying the account with a credit card will increase the number of free dyno hours to 1100. Dynos on the free tier will sleep after 30 minutes of inactivity. Visiting the web address of a sleeping dyno will take longer than usual to render the first request as the dyno is activated from the sleeping state.\n\nWhen an application is ready for production-level availability, simply upgrade the dyno to a paid tier to have the application accessible 24/7. At the time of this writing, paid tiers start at $7 per month per dyno.\n\nIn addition to upgrading application availability, Heroku has an Add-ons marketplace which provides database, cache, and application monitoring services to name a few. These services include industry standard tools specially configured for plug-and-play interfacing with the Heroku platform. Each add-on has it's own tiered pricing system, and there are many with a free tier which match Heroku's free tier on being perfectly suited for learning and prototyping.",
"sectionType": "chapter",
"sequence": 3,
"parentSection": {
"parentSectionPart": 2,
"parentSectionType": "part",
"parentSectionSequence": 2
}
},
{
"title": "Deploying To Heroku",
"slug": "deploying-to-heroku",
"content": "## Deploying to Heroku\n\n### Prerequisites\nHeroku manages application deployments with Git. A Git application repository is Prerequisite 0, and necessary for Heroku deployment. A `smartsite` Git repository was initialized in a previous section, but keep this requirement in mind for any future new build.\n\n1. Create a Heroku account\nTo get started with Heroku, you will need a Heroku account. If you do not yet have a Heroku account, navigate to [heroku.com](https://heroku.com) and create one.\n\n2. Install the Heroku CLI\nHeroku provides a command-line interface so that creating, configuring, and maintaining Heroku applications and add-ons is as easy as a terminal command. This is very powerful functionality that ultimately could be done via Heroku's web interface, yet the Heroku terminal commands are much more concise approach to accomplishing these tasks. Install the [Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli) (https://devcenter.heroku.com/articles/heroku-cli).\n\n### 1. `heroku create`\nRun the `heroku create` command. Use the optional `<appName>` parameter to create a user friendly slug.\n\n<div class=\"filename\">command line</div>\n\n```bash\n$ heroku create <appName>\n```\nReplace `<appName>` with your choice of app name. This affects the application's URL. For example, `heroku create smartsite` will be accessible from the URL `smartsite.herokuapp.com`. The output of the command will list the remote application's URL.\n\n<div class=\"filename\">command line</div>\n\n```bash\n$ heroku create smartsite\nCreating ⬢ smartsite... done\nhttps://smartsite.herokuapp.com/ | https://git.heroku.com/smartsite.git\n````\n\nHeroku subdomains must be unique. If the `<appName>` selected is already in use, you will need to re-run the command with a new name. If no `<appName>` is provided, creates the app with a random slug. For example `heroku create` with no name specified will create the URL with a random subdomain such as `sleepy-meadow-81798.herokuapp.com`.\n\nNotice as well that the output of the `create` command also lists the URL for the application's Heroku Git repository. This is Heroku's copy of the filesystem to which `git commit`ed changesets will be applied.\n\n### 2. `git commit`\nThe most recent git commit is what will go live on Heroku. If there are local changes to the working directory that are not committed, they will not be made live on Heroku. Keep this in mind, and use it when appropriate. For now, we will save all local changes to the local git repository and sync the changes to Heroku.\n\n<div class=\"filename\">command line</div>\n\n```\n$ git status\n$ git add .\n$ git commit -m 'Create commit'\n```\n\n### 3. Push to Heroku\nThe `heroku create` command added a remote git repository on Heroku's servers. See this new remote by running the command `$ git remote -v`. Push the code to this remote.\n\n<div class=\"filename\">command line</div>\n\n```\n$ git push heroku [branchName]\n```\n\nYou can watch the build logs output in the terminal. Whether the deployment is successful or not is displayed in the terminal at the end of the logs.\n\n### 4. Access the application.\nThe deployed application can be accessed by navigating in the browser to the URL output by Step 1. You can also open the deployed application with the command `heroku open`.\n\n<div class=\"filename\">command line</div>\n\n```bash\n$ heroku open\n```\n\n### Resources\nThe Heroku CLI: [https://devcenter.heroku.com/articles/heroku-cli](https://devcenter.heroku.com/articles/heroku-cli)",
"sectionType": "section",
"sequence": 1,
"parentSection": {
"parentSectionPart": 2,
"parentSectionType": "chapter",
"parentSectionSequence": 3
}
},
{
"title": "Authentication",
"slug": "authentication",
"content": "## Authentication\n**Authentication** is the process of verifying the identity of an individual. User authentication allows developers of a web application to craft individualized experiences. In practice, this means allowing access to priveleged material such as creating database records and visiting . This ability to deliver dynamic content individualized per visitor session is the general differentiator between a web*site* and a web *application*.\n\nThe ability to register a user account with the app, and sign in and out on request is the basis of user authentication. Identity and Access Management systems is a discipline in its own right. It is a foundational component to the interactive internet. Consider the example of a social media platform.\n\nOnce a user logs in, the application can display content based on user preferences and saved data. Consider the FaceBook profile page. Every user of FaceBook can navigate to `facebook.com/profile`, and be presented with a profile page. Despite receiving the same webpage template, the page is customized to display the feed and information of the currently logged in user.\n\nFurther, if a user *is not* a logged in user of FaceBook, the page does not display and instead redirects to registration form. The ability to gate features is an additional benefit of adding an authentication component to a web application.\n\n### Authorization\n**Authorization** is a term closely related to Authentication. Where authentication refers to the ability to verify the user's identity, authorization refers to allowing or restricting a user's access to certain resources.\n\nIn the example of a social media network, a user that has *not* authenticated with the server (i.e. not signed in) will likely be blocked from accessing a `/profile` page. Further, even if a user is signed in, certain actions like updating a different user's profile information is likely prohibited. For Software-as-a-Service (SAAS) web applications, authorization comes in the form of allowing and restricting application features based on a user's payment tier.\n\nWhile the need for authorization is generally universal for web applications, authorization concerns are specific to the \"business logic\" or domain rules of the application. Questions like how many tiers of user access are necessary and what resources should be available to whom are answered in examination of the real-world business rules the application is built to model.",
"sectionType": "chapter",
"sequence": 4,
"parentSection": {
"parentSectionPart": 2,
"parentSectionType": "part",
"parentSectionSequence": 2
}
},
{
"title": "Add Auth",
"slug": "add-auth",
"content": "## Add Authentication with Auth0\n`smartsite` will utilize the Auth0 service for authentication. Auth0 is a drop-in IAM solution to add authentication and authorization services to an application. Notably, it comes with single-sign on which will allow users of `smartsite` to sign up with the social provider (e.g. Google, Apple) of their choice. In addition to the fundamental authentication flow featured in `smartsite` Basics, Auth0 offers further authentication features such as multi-factor authentication, custom landing pages, and multi-domain applications.\n\n### 1. Sign up for Auth0\nAuth0 provides a user interface for configuring applications' authentication settings. Setting up an application in the interface is a step-by-step walkthrough process.\n\n1. Sign up for Auth0's free tier\n2. Navigate to the Applications Dashboard\n3. \"Create Application\". Set name and select \"Regular Web Application\"\n4. Select Node.js from the list of supported frameworks and \"Integrate now\"\n5. Set the \"Allowed Callback URL\"\n * Set this value to `https://localhost:3001/callback`.\n * More the callback URL is covered in the \"Caddy reverse proxy\" section.\n* Set \"Allowed Logout URLs\"\n * Set this value to `https://localhost:3001`\n\n### 2. Add Auth0 to `smartsite`\nNow that the third-party service is configured to accept requests, we must now add code within `smartsite` that makes calls to Auth0's application interface (API). While using raw HTTP calls to accomplish this is possible, `smartsite` will utilize Auth0 provided API wrapping library, `express-openid-connect`. This package abstracts the HTTP routing and configuration to JavaScript functions and classes with developer-friendly interfaces.\n\n1. Install the `express-openid-connect` authentication middleware.\n\n<div class=\"filename\">command line</div>\n\n```\n$ npm install express-openid-connect\n```\n\n2. Copy the configuration code provided by Auth0's Quick Start wizard.\n\nThe Express OpenID library provides a router that defines authentication routes -- `/login`, `/logout`, and `/callback` -- for the application. Under the hood, the package is using the familiar syntax for defining a route for an Express app.\n\n```javascript\napp.get('/login', handleLogin);\napp.get('/logout', handleLogout);\n```\n\nInstead of the end using developer having to define this logic or handling functions, `express-openid-connect` exposes a configuration object interface. The developer simply initializes the router with application specific configuration, and all authentication routing is forwarded to Auth0 as necessary.\n\nBetter yet, Auth0 provides the configuration object and JavaScript snippet within their Quick Start interface. The snippet goes in `index.js`. It requires the `auth` router from `express-openid-connect`, and configures the `auth` router with variables provided by Auth0.\n\n<div class=\"filename\">index.js</div>\n\n```javascript\nconst { auth } = require('express-openid-connect');\n\nconst config = {\n authRequired: false,\n auth0Logout: true,\n secret: 'a long, randomly-generated string stored in env',\n baseURL: 'https://localhost:3001',\n clientID: '[UNIQUE CLIENT ID]',\n issuerBaseURL: 'https://[UNIQUE ID].us.auth0.com'\n};\n\n// auth router attaches /login, /logout, and /callback routes to the baseURL\napp.use(auth(config));\n```\n\nNext, the snippet provides an example route that utilizes the `isAuthenticated` helper method provided by the `auth` middleware. `smartsite` already has a `/` route, so if you intend to keep the example route, rename its path to avoid pathname conflicts.\n\n<div class=\"filename\">index.js</div>\n\n```javascript\n// req.oidc is provided from the auth router\n// isAuthenticated is a method on the req.oidc object\napp.get('/auth-check', (req, res) => {\n res.send(req.oidc.isAuthenticated() ? 'Logged in' : 'Logged out');\n});\n```\n\n### 3. Set up an HTTPS proxy\nNotice that the `baseURL` Auth0 is aware of is `https://localhost:3001`. Auth0 requires authentication traffic be delivered via the HTTP**Secure** protocol. HTTPS is an extension to the HTTP protocol, but includes layers of security via encryption and certificate checking to ensure the identity of web servers.\n\nThe Auth0 configuration is different in two ways from the Express server in `index.js`:\n\n<ol>\n <li>It is served over the <pre>HTTPS</pre> protocol.</li>\n <li>Its port address is 3001.</li>\n</ol>\n\nIn this step, we'll set up a webserver to traffic (i.e. proxy) HTTPS web traffic at port 3001 to the Express server listening at port 3000. When the proxy server is running, the application available at both `http://localhost:3000` and `https://localhost:3001`.\n\nNote: This solution is for local development. The proxy server will not need to be run in production because Heroku defaults to serving all web traffic over HTTPS.\n\nInstall the `@leafac/caddy` npm library as a dev dependency.\n\n<div class=\"filename\">command line</div>\n\n```\n$ npm i --save-dev @leafac/caddy\n```\n\n### 4. Create a start script for the proxy server\n\nAdd a script, `https-proxy`, to `package.json`:\n\n<div class=\"filename\">package.json</div>\n\n```javascript\n\"scripts\": {\n ...,\n \"https-proxy\": \"npx @leafac/caddy reverse-proxy --from localhost:3001 --to localhost:3000\"\n}\n```\n\nThe caddy library defaults to interpreting the `--from` parameter as `https` and the `to` parameter as `http` -- exactly what is needed in this case.\n\nWe can now run `npm run https-proxy` and the proxy server will initialize and forward traffic HTTPS traffic at port 3001 to port 3000. You will have to open seperate terminal windows to run `npm run start` and `npm run https-proxy` concurrently. Alternatively, look into an npm library like [`npm-run-all`](https://www.npmjs.com/package/npm-run-all) for a tool to run both commands from one terminal window.\n\n### 5. Test locally\n1. Within `index.js`, alter `/` route to pass the `isAuthenticated()` boolean to the front end.\n\n<div class=\"filename\">index.js</div>\n\n```javascript\napp.get('/', function(request, response) {\n response.render('index', {\n loggedIn: request.oidc.isAuthenticated()\n });\n});\n```\n\n2. Alter `index.liquid` to show a Logout or Login button depending on whether there is a currently logged in user. Within the list of links:\n\n<div class=\"filename\">index.liquid</div>\n\n```html\n<li>\n {% if loggedIn %}\n <a href=\"/logout\">Logout</a>\n {% else %}\n <a href=\"/login\">Login</a>\n {% endif %}\n</li>\n``` \n\n3. To see the full changes, first run the Express and Caddy servers (`npm run start` and `npm run dev-proxy` respectively.) Next, open a browser to `localhost:3001`, and navigate through the authentication flow.\n\n<ol>\n <li>Click Login</li>\n <li>Authenticate with Auth0</li>\n <li>Be redirected back to the base URL, <pre>localhost:3001</pre>.</li>\n <li>Click Logout</li>\n</ol>\n\n### 6. Git commit the changes\nThis was a significant unit of development. The server now has the ability to authenticate users, albeit only for the local environment. We will look at authentication for the deployed environment in the next section. For now, `git commit`!\n\n<div class=\"filename\">command line</div>\n\n```\n$ git add .\n$ git commit -m 'Add auth in development'\n```\n\n### Resources\nAuth0: https://auth0.com/docs/\n\nAuth0 Explainer Video: https://auth0.com/resources/videos/auth0-explainer-video\n\nAuth0 Express: https://auth0.com/docs/quickstart/webapp/express\n\nHTTPS in Development: https://auth0.com/docs/libraries/secure-local-development\n\nRun Node Commands Simultaneously: https://itnext.io/4-solutions-to-run-multiple-node-js-or-npm-commands-simultaneously-9edaa6215a93",
"sectionType": "section",
"sequence": 1,
"parentSection": {
"parentSectionPart": 2,
"parentSectionType": "chapter",
"parentSectionSequence": 4
}
},
{
"title": "Auth Deployed",
"slug": "auth-deployed",
"content": "## Authentication in the Deployed Environment\nTo get authentication accessible to an internet audience, we will have to get the feature live in a deployed environment. After some minor changes to the Auth0 configuration and application code, the application will be ready for deploy to Heroku.\n\nThe application, both within the code and within the Auth0 interface, is currently configured to use `localhost` addresses for callbacks and redirects. The base URL in the deployed state will be different -- `<your_app_slug>.herokuapp.com` if you are following this walkthrough.\n\nThe first approach to look at is using the same Auth0 application for local development and in the deployed environment. There are use cases for this method, but it is not the most robust solution.\n\n### Authentication Using the Same Auth0 Application\nWithin the Auth0 interface, the application will need to be configured to listen for traffic coming from both the development server *and* the live deployment server. Notably, these servers have different root URLs. \n\n### 1. Modify the Auth0 configuration\n\nAdd `https://<your-app>.herokuapp.com` alongside the `https://localhost` entries.\n\n[image callback_urls.png]\n\nThe \"Allowed Callback URLs\" and \"Allowed Logout URLs\" fields accept comma-separated values. Be sure to use `https` as you type these values. Save changes.\n\n### 2. Modify the Express server\nWithin `index.js`, alter the Auth0 configuration to conditionally use the Heroku URL as the application's `baseURL`.\n\n<div class=\"filename\">index.js</div>\n\n```javascript\n// Auth0\nconst config = {\n baseURL:\n process.env.NODE_ENV == 'production' ? 'https://<your-app>.herokuapp.com' : 'https://localhost:3001',\n ...\n};\n```\n\n### 3. Modify the Procfile\nThe conditional added in step 2 evaluates to true if the `NODE_ENV` environment variable is set to `'production'`. If the env variable is not set or is set to a different value, the conditional will evaluate to false. We will force the environment variable to be set to `'production'` when Heroku starts the server process.\n\nA common method of providing environment variables to a process is to define them immediately before the process command. The `Procfile` contains the command Heroku uses to start the web server. Define `NODE_ENV` at the start of the `web` process.\n\n<div class=\"filename\">Procfile</div>\n\n```\nweb: NODE_ENV=production npm run start\n```\n\n4. Deploy the application to Heroku.\n`git add` and `commit` all changes, then push the Git repository to Heroku.\n\n<div class=\"filename\">command line</div>\n\n```\n$ git add .\n$ git commit -m 'Add auth for Heroku'\n$ git push heroku master\n```\n\nWhen the deploy is finished and if all is configured properly, the application will be available at its deployed URL with the authentication feature. Use the `heroku open` utility to open the app in a web browser.\n\n<div class=\"filename\">command line</div>\n\n```\n$ heroku open\n```\n\n### Resources\nEnvironment Variables in Node.js: [https://www.twilio.com/blog/working-with-environment-variables-in-node-js-html](https://www.twilio.com/blog/working-with-environment-variables-in-node-js-html)",
"sectionType": "section",
"sequence": 2,
"parentSection": {
"parentSectionPart": 2,
"parentSectionType": "chapter",
"parentSectionSequence": 4
}
},
{
"title": "Environment Variables",
"slug": "environment-variables",
"content": "## Environment Variables\nThe need for environment specific configuration is inherent in developing a web application. Environment variables are variables that describe the environment in which the application or script is being invoked. `smartsite` is currently configured to run in two environments: a developer's local system and Heroku for production. As we encountered in the last section, a web application will likely be required to have different configuration for the different environments.\n\nSpecifically, the application needed to be aware of which environment it was running in to know with what `baseUrl` to configure the Auth0 middleware.\n\n<div class=\"filename\">index.js</div>\n\n```javascript\nconst config = {\n baseURL:\n process.env.NODE_ENV == 'production' ? 'https://<your-app>.herokuapp.com' : 'https://localhost:3001',\n ...\n};\n```\n\nEnvironment variables are the solution for the differing configuration variables, yet the implementation varies. Currently, `smartsite` uses an inline ternary operator to determine a selection between two publically available datum. This solution a) does not scale, and b) will not work for datasets we would not like to be publically exposed.\n\nTo get a good grasp on the issues of scalability and security, let's look at the example of adding a new application configuration that will need to be different per environment. For illustration, we'll consider adding database credentials. A database connection requires the Node.js application to be configured with a username, password, and host URL to authenticate with the database server. These values will be different between environments.\n\nAs a look ahead, the foundations covered here will be implemented in the **Database** section.\n\n### Scalability\nBecause `smartsite` runs in only two environments, checking if an environment variable is one of the possible values is able to be accomplished in one line. But what if there were more than two environments? To use the example of adding database credentials, the application will need to be configured for development, production and *test* environment -- this would require a check for one of three values.\n\n<div class=\"filename\">pseudocode</div>\n\n```\nconst dbHost = process.env.NODE_ENV == 'production' ? 'production.database' : process.env.NODE_ENV == 'test' ? 'test.database' : 'development.database';\n```\n\nThis process is barely legible using ternary operators, and a `switch` statement is likely more suited:\n\n<div class=\"filename\">pseudocode</div>\n\n```javascript\nlet dbHost;\nswitch(process.env.NODE_ENV) {\n case 'production':\n dbHost = 'production.database'\n case 'test':\n dbHost = 'test.database'\n default:\n dbHost = 'development.database'\n}\n```\n\nThis is barely more readable. Developers must now contend with minimum 9-line `switch` statements for each database configuration variable. If this pattern continues into future development, each new database, third-party integration or *<reason>* to use environment variables will come with the same burden of clutter.\n\nA solution for scalability looks like keeping configuration variables stored in environment-specific objects. When the application boots up, select the appropriate configuration object based on `NODE_ENV`.\n\n<div class=\"filename\">pseudocode</div>\n\n```javascript\n// index.js\nconst environments = {\n \"development\": {\n \"dbUser\": \"popdemtech\",\n \"dbPassword\": \"popdemtech123\",\n \"dbName\": \"smartsite\",\n \"dbHost\": \"localhost:5432\"\n },\n \"production\": {\n \"dbUser\": \"smartsite\",\n \"dbPassword\": \"myappXYZ123\",\n \"dbName\": \"smartsite\",\n \"dbHost\": 'heroku-postgres://smartsite',\n },\n \"test\": {\n \"dbUser\": \"popdemtech\",\n \"dbPassword\": \"popdemtech123\",\n \"dbName\": \"smartsite-test\",\n \"dbHost\": \"localhost:5432\"\n }\n};\n\nconst config = environments[process.env.NODE_ENV] || environments['development'];\n```\n\nBy collecting variables into environment specific objects, using environment variables within application scripts is as simple as accessing the appropriate property on the environment specific `config` object. Because checking `NODE_ENV` occurs at the point of selecting the correct `config` object, the need for `switch` statments and ternary operators is removed.\n\n<div class=\"filename\">pseudocode</div>\n\n```javascript\n\nconst dbHost = process.env.NODE_ENV == 'production' ? 'production.database' : process.env.NODE_ENV == 'test' ? 'test.database' : 'development.database';\n\n// turns into\n\nconst dbHost = config.dbHost;\n```\n\nThis solves scalability in the following ways:\n\n1. As new **environment variables** are added, the process for development is to add the variable to each environment's object, and access the variable within the JavaScript as `config.<property>`.\n\n2. As new **environments** are added -- such as a remote test environment for a continuous integration workflow -- configuring a environment is as simple as adding a new top-level configuration object, and setting `NODE_ENV` to the proper value at server initialization.\n\n3. There is no complex logic involved in determining the correct set of environment variables to use.\n\n### Security\nThe need for security arises when a web application needs access to data that should not be checked into source code. This is clearly illustrated in the example database credentials within the `environments` object:\n\n<div class=\"filename\">pseudocode</div>\n\n```javascript\nconst environments = {\n \"production\": {\n \"dbUser\": \"smartsite\",\n \"dbPassword\": \"myappXYZ123\",\n \"dbName\": \"smartsite\",\n \"dbHost\": 'heroku-postgres://smartsite',\n }\n};\n```\n\nIf this code is checked into the source repository using `git commit`, a nefarious internet abuser would be able to impersonate the application, and gain full access to the database. While there are many safeguards in place to encrypt transmissions carrying the source code, e.g. HTTPS, there are many areas of the deployment pipeline where application code is readable in plaintext.\n\n**Do not save private keys and passwords to source code.** More than a best practice, this is standard operating procedure for organizational and user security.\n\nWe have been speaking of environment variables in terms of their use in a web application, but have glossed over their practical feature as it pertains to application development at large. An environment variable, conventionally written in `ALL_CAPS` snakecase, is set at the *operating system* or *process* level. This is external to the JavaScript application. The `process.env` variable within Node.js has access to these lower-level variables.\n\nAfter setting the variables within the operating system environment, using these variables within the app is as simple as `process.env`.\n\n<div class=\"filename\">pseudocode</div>\n\n```javascript\nconst environments = {\n \"production\": {\n \"dbUser\": process.env.DB_USER,\n \"dbPassword\": process.env.DB_PASS,\n \"dbName\": process.env.DB_NAME,\n \"dbHost\": process.env.DB_HOST,\n }\n};\n```\n\nWhile the secure variable now will be defined external to the application, an open question remains: *\"Where will environment variables be defined?\"*\n\nFor local development, a file that's been added to `.gitignore` suffices. Heroku, the deployed environment, provides both a CLI and graphic interface for entering custom environment variables.\n\n### Review\nEnvironment variables provide a standard interface for developers to specifiy configuration on a per-environment basis. By extracting environment-dependent configuration into a structured object, we simplify the application code. The code is simplified by a reduction of logic and a compacting of locations where environment variable data can be found.\n\nEnvironment variables are also used to keep secure data out of application logic. By defining and providing variables *external* to the application, these values are not accessible to unethical hackers who may get access to source code.\n\n### Resources\nEnvironment variable: [https://en.wikipedia.org/wiki/Environment_variable](https://en.wikipedia.org/wiki/Environment_variable)\n\nWorking with Environment Variables in Node.js: [https://www.twilio.com/blog/working-with-environment-variables-in-node-js-html](https://www.twilio.com/blog/working-with-environment-variables-in-node-js-html)\n\nThe Twelve Factor App (Config): [https://12factor.net/config](https://12factor.net/config)\n",
"sectionType": "chapter",
"sequence": 5,
"parentSection": {
"parentSectionPart": 2,
"parentSectionType": "part",
"parentSectionSequence": 2
}
},
{
"title": "Using Env Vars",
"slug": "using-env-vars",
"content": "## Using Environment Variables\nAs covered in the last section, environment variables are used to capture environment specifc configuration. We will use the conventions outlined in the previous section to handle environment variables in `smartsite`.\n\nThe Node.js community has converged on the `dotenv` library to manage environment variables in application code. We will add `dotenv` to the application and use environment variables to manage Auth0 configuration variables.\n\n### 1. Install dotenv\nDotenv will be used in all environments. Save it as an application dependency.\n\n<div class=\"filename\">command line</div>\n\n```\n$ npm install dotenv\n```\n\n### 2. Create .env\nDotenv works by reading a file named `.env` located in the root directory. Variables defined in `.env` using the format `VARIABLE_NAME=\"value\"` are read into the application and made available on the `process.env` object.\n\n1. Create `.env`\nIn the root directory, create a new file named `.env`. In this file, define a variable for Auth0 base URL.\n\n<div class=\"filename\">.env</div>\n\n```\nAUTH0_BASE_URL=\"https://localhost:3001\"\n```\n\n### 3. Use the variables in the application\nUsing the dotenv library within the web application involves importing the library, and calling it's `config()` method. This method call attaches the variables to `process.env`, and should be invoked as early in the application script as necessary.\n\n1. Require `dotenv`\n\n<div class=\"filename\">index.js</div>\n\n```javascript\nrequire('dotenv').config();\n```\n\n2. Set `baseUrl` to the environment variable\nReplace the ternary operation with the `AUTH0_BASE_URL` environment variable.\n\n<div class=\"filename\">index.js</div>\n\n```javascript\n// Auth0\nconst config = {\n ...\n baseURL: process.env.AUTH0_BASE_URL,\n ...\n};\n```\n\n3. Restart the server\nTest that the new configuration works by restarting the server and walking through the authentication steps. Remember to start the HTTPS proxy in a separate terminal window. Expect the features to work as before.\n\n<div class=\"filename\">command line</div>\n\n```\n$ npm run start\n$ npm run https-proxy\n```\n\n### 3. Migrate Auth0 configuration\nUsing the same process as outlined, we will migrate more Auth0 configuration to the `.env` file. The criteria used to determine which properties should be managed as environment variables are whether a) the property is configurable within the Auth0 dashboard, b) the property is likely to change if a different Auth0 application is used as a backend, and c) if the data should be kept secure.\n\n* Configurable within Auth0\n * `baseURL`\n * `routes.callback`\n* Different per Auth0 application\n * `clientID`\n * `issuerBaseURL`\n* Should be kept secure\n * `secret`\n\n1. Add the specified data to `.env`\n\n<div class=\"filename\">.env</div>\n\n```\nAUTH0_BASE_URL=\"https://localhost:3001\"\nAUTH0_CALLBACK_ROUTE=\"/auth0/callback\"\nAUTH0_CLIENT_ID=\"BdsyUqLCLcMDv21lT9VzCRuo8fP2xvZl\"\nAUTH0_ISSUER_BASE_URL=\"https://dev-r6lb7q89.us.auth0.com\"\nAUTH0_SECRET=\"a long, randomly-generated string stored in env\"\n```\n\n2. Use environment variables to configure Auth0\nReplace the hard-coded strings used to configure the Auth0 middleware with `process.env` variables.\n\n<div class=\"filename\">index.js</div>\n\n```javascript\n// Auth0\nconst config = {\n authRequired: false,\n auth0Logout: true,\n secret: process.env.AUTH0_SECRET,\n baseURL: process.env.AUTH0_BASE_URL,\n clientID: process.env.AUTH0_CLIENT_ID,\n issuerBaseURL: process.env.AUTH0_ISSUER_BASE_URL,\n routes: {\n callback: process.env.AUTH0_CALLBACK_ROUTE\n }\n};\n```\n\n### 4. Generate a `secret` string\nThe Auth0 configuration object's `secret` key is used to encrypt the user's session cookie. This value should be a \"long, random string.\" Technically, the value provided by Auth0's Quick Start satisfies this constraint. To generate a more random string, Auth0 recommends the OpenSSL command line utility.\n\n<div class=\"filename\">macOS/*nix command line</div>\n\n```\n$ openssl rand -hex 32\n```\n\n<div class=\"filename\">Windows command line</div>\n\n```\n> & 'C:\\Program Files\\Git\\usr\\bin\\openssl.exe' rand -hex 32\n```\n\nIf none of the above options work, an online random string generator will do.\n\n3. Replace the environment variable with the output from the command.\n<div class=\"filename\">.env</div>\n\n```\nAUTH0_SECRET=\"ce0b8df696236657682ca78c233b174b1d7581761467270256d778baf1fb9cd5\"\n```\n\n### 5. Restart the server\nChanges to the `.env` file do not trigger the `nodemon` filesystem watcher to restart the Express server. Stop and restart the server process for the application to reflect changes to the `.env` file.\n\n<div class=\"filename\">command line</div>\n\n```\n$ npm run start\n```\n\nThe authentication flow should perform as usual.\n\n### 6. Add a Route\nAdd to the chronicle of `smartsite` by adding a webpage that renders dynamic content based on environment variables.\n\n1. Create a `GET` route\nDefine a route on the Express app that renders a template, `env-vars.liquid`, with local variables.\n\n<div class=\"filename\">index.js</div>\n\n```javascript\napp.get('/env-vars', function (request, response) {\n const isProduction = process.env.NODE_ENV == 'production';\n const auth0BaseUrl = process.env.AUTH0_BASE_URL;\n response.render('env-vars', { isProduction, auth0BaseUrl })\n});\n```\n\n2. Create the view template\nDisplay the `isProduction` and `auth0BaseUrl` local variables within a simple template.\n\n<div class=\"filename\">app/views/env-vars.liquid</div>\n\n```html\n{% layout 'layouts/default-html.liquid' %}\n{% block content %}\n<div>\n <h1>Environment Variables</h1>\n\n <p>\n This webpage has been\n {% if isProduction %}\n <b> served from the production environment.</b>\n {% else %}\n <b> served from a non-production environment.</b>\n {% endif %}\n </p>\n\n <p>\n The Auth0 base URL is\n {% if auth0BaseUrl %}\n <b>{{ auth0BaseUrl }}</b>\n {% else %}\n <b> not defined.</b>\n {% endif %}\n </p>\n</div>\n{% endblock %}\n```\n\n3. Add this new page to the site navigation in `index.liquid`.\n\n<div class=\"filename\">app/views/index.liquid</div>\n\n```html\n<li><a href=\"/env-vars\">env-vars</a></li>\n```\n\nWith your development server started, you can now navigate to `/env-vars` from the home page and visualize some of the application's environment variables.\n\n[env-vars-webpage.png image]\n\nThis simplistic page is for demonstration purposes; the primary takeaway should be the new abilities to set application configuration on a per-environment basis.\n\n### 6. Distribute Environment Variables\nEnvironment variables should be kept out of version control. If you used the `.gitignore` provided by this walkthough, `.env` is already included to be ignored.\n\nDespite keeping the sensitive information out of the version control, developers will need a way to share a list of what environment variables are required to configure the application. The standard solution to this is to distribute a `.env.dist` file with source control which contains the environment variable names, but not the sensitive values.\n\nThe `.env.dist` file should be kept up to date as more environment variables are added to the application. This file can be used to onboard new developers to the project, as well it records a list of what environment variables need to be set for the application to run properly in any new environment.\n\n1. Ensure `.env` is an ignored file in `.gitignore`.\n\n2. Create a `.env.dist` file\nCreate the new file mirroring `.env`, but with the sensitive values removed. Discernment dictates the only value that raises security flags is `AUTH0_SECRET`, so its value will be redacted.\n\n<div class=\"filename\">.env.dist</div>\n\n```\nAUTH0_BASE_URL=\"https://localhost:3001\"\nAUTH0_CALLBACK_ROUTE=\"/auth0/callback\"\nAUTH0_CLIENT_ID=\"BdsyUqLCLcMDv21lT9VzCRuo8fP2xvZl\"\nAUTH0_ISSUER_BASE_URL=\"https://dev-r6lb7q89.us.auth0.com\"\nAUTH0_SECRET=\n```\n\n### 7. Git commit all changes\nEnvironment variables configuration is now complete, and the application is working locally. Time to `git commit` the changes in preparation for deployment to Heroku.\n\nUse the command `git status` before running `git add` to ensure `.env` is not saved to version control. `.env.dist` should be listed as an \"untracked file\".\n\n<div class=\"filename\">command line</div>\n\n```\n$ git status\n$ git add .\n$ git commit -m 'Use Auth0 environment variables'\n```\n\n### Resources\nDotenv: [https://www.npmjs.com/package/dotenv](https://www.npmjs.com/package/dotenv)\n\nOpenSSL on Windows: [https://stackoverflow.com/a/68253950/18752242](https://stackoverflow.com/a/68253950/18752242)\n\nRandom String Generator: [https://www.random.org/strings/](https://www.random.org/strings/)\n",
"sectionType": "section",
"sequence": 1,
"parentSection": {
"parentSectionPart": 2,
"parentSectionType": "chapter",
"parentSectionSequence": 5
}
},
{
"title": "Env Vars In Production",
"slug": "env-vars-in-production",
"content": "## Environment Variables in Production\nA cursory glance at the code changes in the last section reveals that we have removed references to `https://<your-app>.herokuapp.com`, but have yet to replace this value. When `require('dotenv').config()` is invoked, *no values will be appended to* `process.env` *since there is no* `.env` *file in the filesystem pushed to Heroku.*\n\nDifferent deployment strategies have different requirements for how environment variables are set. Deployment onto a remote Linux machine may require accessing the server via SSH and manualy creating a `.env` file in the remote environment. Containerized deployment often uses platform-specific configuation files -- such as `docker-compose.yml` for organizing the environment.\n\nHeroku's platform-as-a-service solves deployed environment variables in simple terms. Each Heroku application comes with a Settings page for configuring variables within Heroku's web UI. As well, the `heroku` CLI utility provides a one-line command for setting environment variables remotely.\n\n### Heroku Configuration Variables: Graphic Interface\nHeroku calls environment variables \"Config Vars,\" and these can be found within an application's Settings page.\n\n[heroku application settings.png]\n\nClick \"Reveal Config Vars\" button to show all variables and reveal input fields to edit them. If there are no Config Vars, a descriptive message will be shown. In both cases, a developer can add a new configuration variable directly in this interface by entering a new `KEY` and `VALUE`, and clicking \"Add\".\n\n[heroku empty config vars.png]\n\nFeel free to use the graphic interface to set environment variables. The `smartsite` walkthrough details using Heroku's CLI to accomplish this task.\n\n### Heroku Config Vars: Command-Line Interface\nThe Heroku CLI allows developers to manage Heroku apps directly from the terminal. An app's config vars are accessible via the subcommand `config`. Issuing the command `heroku config --help` displays options for the subcommand.\n\n<div class=\"filename\">command line</div>\n\n```\n$ heroku config --help\ndisplay the config vars for an app\n\nUSAGE\n $ heroku config\n\nOPTIONS\n -a, --app=app (required) app to run command against\n -j, --json output config vars in json format\n -r, --remote=remote git remote of app to use\n -s, --shell output config vars in shell format\n\nCOMMANDS\n config:edit interactively edit config vars\n config:get display a single config value for an app\n config:set set one or more config vars\n config:unset unset one or more config vars\n```\n\n### 1. Set a Config Var\nIssue the command `heroku config:set` to set the `NODE_ENV` configuration variable to `production`.\n\n**Note:** Remember what Heroku calls \"config vars\" are more provided to the running application as environment variables.\n\n<div class=\"filename\">command line</div>\n\n```\n$ heroku config:set NODE_ENV=production\n```\n\nCheck that the variable is set by issuing the command `heroku config` with no options.\n\n### 2. Set Auth0 Configuration\nUse the sme process to set a config var for each of the Auth0 environment variables. The one variable which must be different from local configuration is `AUTH0_BASE_URL`. The example commands cover two methods of setting the variables: one at a time and many at once.\n\n<div class=\"filename\">command line</div>\n\n```\n$ heroku config:set AUTH0_BASE_URL=https://<your-app>.herokuapp.com\n$ heroku config:set AUTH0_CALLBACK_ROUTE=/auth0/callback\n$ heroku config:set AUTH0_CLIENT_ID=<your-client-id> AUTH0_ISSUER_BASE_URL=<your-issuer-url> AUTH0_SECRET=<your-secret>\n```\n\nCheck that the variables are set by issuing the command `heroku config` with no options.\n\n### 3. Deploy to Heroku\nNow that the Heroku is aware of the environment variables the Node.js application requires, it is safe to deploy to Heroku without causing an application crash.\n\n<div class=\"filename\">command line</div>\n\n```\n$ git push heroku master\n$ heroku open\n```\n\nAfter the deployment process, run `heroku open` to open the Heroku app. Test that all changes are successful by traversing the authentication flow. Navigate to the `/env-vars` route to see the programmed display messages.\n\n### Resources\nHeroku config CLI: [https://devcenter.heroku.com/articles/config-vars](https://devcenter.heroku.com/articles/config-vars)",
"sectionType": "section",
"sequence": 2,
"parentSection": {
"parentSectionPart": 2,
"parentSectionType": "chapter",
"parentSectionSequence": 5
}
},
{
"title": "Databases",
"slug": "databases",
"content": "## Databases\n\nEnd-users of an application are more engaged if the web application is dynamic. This dynamism can be accomplished with presentational logic alone, yet an improvement to user engagement a web application can provide is if data state can be saved and retrieved across user sessions.\n\nAs an example, imagine a feature that allows the user to log in and create an invoice. A user can log in and create a document with this Invoice Creator. The usefulness of the Invoice Creator application can be increased for the user if, the next time they log in, they see a list of previously created documents. A further increase in feature set -- such as the ability to edit previously saved documents -- is an increase of usability.\n\nA solution to saving and retrieving user data is to save this data in a database.\n\nApproaching the term from a general sense, a database is for persistent data storage. Data storage can be handled in many ways. At the most basic, pen and paper or Google Sheets suffice. Different technical solutions may require different paradigms of database solutions.\n\nA solution to saving and retrieving user data is to save this data in a persisted storage database. This data stays in the application's database when the user is logged in or not, and can be retrieved by the user interacting with the web page or by a developer running analytics on the data.\n\n\n### Deciding on Database Software\nTo reiterate, a database is for persistent data storage. Data storage can be handled in many ways. At the most basic, pen and paper or Google Sheets suffice. Different technical solutions may require different paradigms of database solutions -- such as relational, graph or time-series.\n\n[image of relational and influxdb architecture]\n\nThe question to answer when deciding on a products database technology is, as usual:\n\nWhat is it for?\n\n* What types of data will go in the database?\n* Are there clear domain models or is there simply a need to set and retrieve singular datum?\n* Is this data clearly represented by relationships between the domain models (e.g. books and authors)?\n* Will the data need to be queried for time-series (e.g. a graph display temperature sensor measurements)\n* Do the objects of a given domain model always have the sames attributes or can different objects have different attribute structures?\n* Who is the end user of the queries for the database -- end-users of the product or business analytics tools like Tableau?\n\nFinally, consider overarching questions that relate to development and maintance of the database:\n* What is the budget for the database?\n* What is the time-budget for implementing a database? A developer will have more speed with technology they are already familiar with.\n* What is the experience of maintainers with the technology?\n* Is there a solution offered on the technology platform already in production (e.g. Heroku, AWS)?\n\nFrom questions such as these, parse out the top three considerations that are important to the decision.\n\nFor `smartsite`, three requirements stick out:\n\n* Heroku add-ons: `smartsite` already deployed on Heroku. A single-click, Heroku add-on will be ideal.\n* Free tier: `smartsite` is an open-source walkthrough that offers introductory Node.js guidance for developers of all budgets.\n* SQL: SQL is **the** industry standard language of relational databases. It's likely a builder of `smartsite` is already familiar with its syntax, and, if not, there are decades worth of resources to extend one's knowledge of it.\n\n`smartsite` will use PostgreSQL as its persistent database. Postgres is a open-source and battle-tested SQL based database server and library. There is a Heroku add-on, Heroku Postgres, that will be used for the production database. Locally, developers will need to install the Postgres database.",
"sectionType": "chapter",
"sequence": 6,
"parentSection": {
"parentSectionPart": 2,
"parentSectionType": "part",
"parentSectionSequence": 2
}
},
{
"title": "Install Postgres Windows",
"slug": "install-postgres-windows",
"content": "## 1. Install PostgreSQL on Windows\nThere is a PostgreSQL installer distributed by Enterprise DB (EDB), an enterprise-level Postgres solution. Download the Windows installer from EDB, and follow the steps. https://www.enterprisedb.com/downloads/postgres-postgresql-downloads\n\nKeep note of what is set as the installation directory. The default location is a `C:\\Program Files\\PostgreSQL\\[##]` directory, where `[##]` is the numerical version number of the installation. For example, the installation directory for version 14 is `C:\\Program Files\\PostgreSQL\\14\\`.\n\nKeep note of the password set for the default user as well.\n\n### 2. Configure binary paths\nThe PostgreSQL installation comes with a library of binary executables. These executables, such as `psql`, `pg_dump`, and `createdb`, live within the `/bin` directory of the installation folder, and are how a computer user or different program can interact with the database server. The binary path will be the installation directory from Step 1 suffixed with `\\bin\\`.\n\nFor example, the default binary path for version 14 is `C:\\Program Files\\PostgreSQL\\14\\bin`.\n\n**Note:** Within any software package or application, binary files and executables are conventionally placed within a directory named `\\bin\\`. PostgreSQL follows this convention.\n\n#### Add the binary path to `$PATH`\nTo be able to interact with the database servers, we will need to be able to run the binary exectuables from the terminal. For this, add the binary path to the system's `$PATH` variable\n\n1. Search Windows for the Edit System Environment Variables dialog by pressing Windows key and typing \"environment variables\". Select the result, and a System Properties dialog should appear.\n\n[env-vars-windows.png]\n\n2. Click \"Environment Variables...\" at the bottom of the dialog.\n3. The Environment Variables window is split top and bottom as \"User variables\" and \"System variables\". Within \"System variables,\" double-click the row for the variable name \"Path\".\n\n[3_env-vars.png]\n\n4. Within the Edit environment varibale window that appearch, click the New button, and paste the binary path for PostgreSQL.\n5. Click OK on each of the windows opened for this process.\n6. The PostgreSQL executables have been added to the `$PATH`. To test this, start a new terminal instance and `psql` to see the output of the command.\n\n\n#### Add the binary path to pgAdmin\nThe EDB Installation wizard installs the pgAdmin program, a graphical interface for PostgreSQL. This is can be an alternative interface to access database servers, databases, tables, and other\n* Login to pgAdmin as the default user, `postgres`, using the password set within the installation wizard.\n* Open Preferences dialog, and add the binary path to the version of Postgres you have installed. The location for this is found at File > Preferences > Paths > Binary Paths > PostgreSQL Binary Path > [YOUR_VERSION].\n\n[pgadmin_binary_paths.png]\n\n### 3. Start the Database\n\n1. Start a database server\nThe `pg_ctl` command is used to manage Postgres database servers. Start and stop a database server by specifying the data directory, and supplying the `start` or `stop` subcommand, respectively. The data directory was set in the installation wizard. It defaults to `<postgresql_installation_directory>\\data\\`.\n\n<div class=\"filename\">command line</div>\n\n```\n> pg_ctl restart -D C:\\Program Files\\PostgreSQL\\14\\data\\\n> pg_ctl stop -D C:\\Program Files\\PostgreSQL\\14\\data\\\n> pg_ctl start -D C:\\Program Files\\PostgreSQL\\14\\data\\\n```\n\n### 4. Create a non-default user\nThe `createuser` command is used to create PostgreSQL users. Note that this is a separate list of users than the Windows login users. For example, it is common to create a separate user per software application with database access.\n\n<div class=\"filename\">command line</div>\n\n```\n> createuser --superuser --pwprompt --username=postgres $Env:Username\n```\n\nThis command:\n* creates a user\n* with superuser privileges\n* prompts for the user's password after creation\n* connects to the database server as the `postgres` user\n* sets the user's name to `$Env:Username`, an environment variable within Windows Terminal\n\n### 5. Create a non-default database\nThe `createdb` command is used to create PostgreSQL databases. The database server serves a \"database cluster.\" A database cluster collection of databases that is managed by a single instance of a running database server. In file system terms, it is a single directory in which all data will be stored (i.e. Postgres' `/data` directory.)\n\nThe PostgreSQL installer created a default database named `postgres`. It is convention for each software program to have its own, uniquely-named database. For practice and utility with the `psql` command in upcoming sections, create a new database named after your Windows user.\n\n<div class=\"filename\">command line</div>\n\n```\n> createdb $Env:Username --username=$Env:Username\n```\n\nThis command:\n* creates a database\n* named after the logged in user\n* using the PostgreSQL user named after the logged in user\n\n### 6. Test the Installation\nIf all has gone well, you have the PostgreSQL command-line tools, a running database server, and a user and database within that server. Test all of these by issuing the `psql` command from the command line. This command defaults to connecting with a username of the currently logged in user, and connecting to a database with the same name as the logged in user. This simple command will test all three aspects of installation.\n\n<div class=\"filename\">command line</div>\n\n```\n> psql\n```\n\n### References\nManaging Postgres users and privileges: [https://kb.objectrocket.com/postgresql/how-to-list-users-in-postgresql-782](https://kb.objectrocket.com/postgresql/how-to-list-users-in-postgresql-782)\n\nPostgreSQL Security Best Practices: [https://resources.2ndquadrant.com/hubfs/Whitepaper PDFs/PostgreSQL_Security_Best_Practices_Whitepaper.pdf](https://resources.2ndquadrant.com/hubfs/Whitepaper%20PDFs/PostgreSQL_Security_Best_Practices_Whitepaper.pdf)\n\nHow to start PostgreSQL on Windows: [https://stackoverflow.com/questions/36629963/how-can-i-start-postgresql-on-windows](https://stackoverflow.com/questions/36629963/how-can-i-start-postgresql-on-windows)",
"sectionType": "section",
"sequence": 1,
"parentSection": {
"parentSectionPart": 2,
"parentSectionType": "chapter",
"parentSectionSequence": 6
}
},
{
"title": "Install Postgres Mac",
"slug": "install-postgres-mac",
"content": "## Install PostgreSQL on Mac with Homebrew\n\nHomebrew is a popular package manager for MacOS. A package manager provides the ability to quickly install packages, their dependency packages, and keep the packages up to date. \"Packages\" are software libraries and executables generally runnable from a command-line interface.\n\n1. Install Homebrew\nWhile we will use Homebrew to install PostgreSQL and its dependencies, we first need to install the Homebrew package itself. If you do not already have Homebrew installed, run the following from a MacOS terminal:\n\n<div class=\"filename\">command line</div>\n\n```bash\n$ /usr/bin/ruby -e \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)\"\n```\n\n2. Install PostgreSQL\n\n<div class=\"filename\">command line</div>\n\n```bash\n$ brew update\n$ brew install postgresql\n$ postgres --v\n```\n\n3. Create a database cluster\nA database storage area on disk must be initialized before. A database cluster is a collection of databases that is managed by a single instance of a running database server. In file system terms, it is a single directory in which all data will be stored. There is no default location for this to be stored; we will set the location to be `/usr/local/var/postgres`:\n\n<div class=\"filename\">command line</div>\n\n```bash\n$ initdb /usr/local/var/postgres\n```\n\nYou may see the error message: `initdb: directory \"/usr/local/var/postgres\" exists but is not empty`. It means the folder you are attempting to create already exists. You are safe to move on to the next step.\n\n4. Start the database server\nUse the command [`pg_ctl`](https://www.postgresql.org/docs/current/app-pg-ctl.html) to control PostgreSQL database servers. The parameter to the `-D` flag indicates the data directory. Use the data directory created in the previous step via `initdb`.\n\n<div class=\"filename\">command line</div>\n\n```bash\n$ pg_ctl -D /usr/local/var/postgres start\n```\nThis will log the initialization processes, output `server started`, and return function of the CLI to the user. This command started the database process in the background. To stop the database process, run\n\n<div class=\"filename\">command line</div>\n\n```bash\n$ pg_ctl -D /usr/local/var/postgres stop\n```\n\n5. Create a database\nWithin the database cluster, the `initdb` command created a database named `postgres`. Make an additional database named by your MacOS username with the following command:\n\n<div class=\"filename\">command line</div>\n\n```bash\n$ createdb $USER\n```\n\n### References\nManaging Postgres users and privileges: [https://kb.objectrocket.com/postgresql/how-to-list-users-in-postgresql-782](https://kb.objectrocket.com/postgresql/how-to-list-users-in-postgresql-782)\n\nPostgreSQL Security Best Practices: [https://resources.2ndquadrant.com/hubfs/Whitepaper PDFs/PostgreSQL_Security_Best_Practices_Whitepaper.pdf](https://resources.2ndquadrant.com/hubfs/Whitepaper%20PDFs/PostgreSQL_Security_Best_Practices_Whitepaper.pdf)",
"sectionType": "section",
"sequence": 2,
"parentSection": {
"parentSectionPart": 2,
"parentSectionType": "chapter",
"parentSectionSequence": 6
}
},
{
"title": "Using Psql",
"slug": "using-psql",
"content": "## Using `psql`\nThe command `psql` allows the developer to enter into a PostgreSQL command line environment for executing SQL and other tasks involving the data in the database. Despite, the examples in this section using the `$` bash shell prompt, these commands work on Windows as well as Unix-based systems.\n\nTo enter into the postgres shell, use the command `psql` and indicate the database. If `psql` is used with no arguments, a database of the current user's name is assumed.\n\n<div class=\"filename\">command line</div>\n\n```\n$ psql postgres\n```\n\nIn this mode, the command line is prefixed by `[DATABASE NAME]=#`. To see this in action, type `exit` to exit the process for the `postgres` database, and enter into a session with the database named by your username by using `psql` with no arguments.\n\n<div class=\"filename\">command line</div>\n\n```\npostgres=# exit\n\n$ psql # Run this command with no arguments\n\n# List databases\npopdemtech=# \\l\n\n# List users\npopdemtech=# \\du\n```\n\nSee the Reference of this section for more utilities available within the `psql` environment.\n\n7. Get database connection info\nAs part of the NodeJS walkthrough, we will be creating application databases, tables, and queries within the NodeJS application using a JavaScript library specfically for interfacing with the PostgreSQL server.\n\nLike a web server, the PostgreSQL server is accessed via TCP -- that is to say, the web application opens a connection to the database server, requests for data, and receives a response. To successfully connect to the data server, and retrieve data, the web application needs to have record of:\n* The database server host location\n* The specific database's name\n* The user attempting access, and\n* The user's password\n\nThe host for local development is `localhost`. The database name, user name, and password are known by the developer.\n\nTo see connection information, enter the `psql` interface and use the `\\conninfo` command. It will output the database, user, and port of the active `psql` session.\n\n<div class=\"filename\">command line</div>\n\n```\n$ psql databasename\ndatabasename=# \\conninfo\nYou are connected to database \"databasename\" as user \"popdemtech\" via socket in \"/tmp\" at port \"5432\".\n```\n\nA separate database is recommended per web application, and, although user and password can be shared between applications, there are benefits to using unique users and passwords per application as well. This can be called the \"Principle of Least Privilege,\" and revolves around database security.\n\nMost high-level languages (e.g. JavaScript) come with wrapper libraries that handle Postgres database creation. It will likely be necessary to create the `user` and `password` using `psql` or similar utilities.\n\nSee the References for further on PostgreSQL user management and security.\n\n### References\n\nPSQL utilities: [https://www.postgresguide.com/utilities/psql/](https://www.postgresguide.com/utilities/psql/)",
"sectionType": "section",
"sequence": 3,
"parentSection": {
"parentSectionPart": 2,
"parentSectionType": "chapter",
"parentSectionSequence": 6
}
},
{
"title": "Postgres In Node",
"slug": "postgres-in-node",
"content": "## Setting up PostgreSQL in an NodeJS Application\nPostgreSQL is a separate server than the NodeJS web server. It provides a TCP interface to access and modify data in the database. The NodeJS application must be configured to connect to a PostgreSQL database server.\n\nOnce the connection with PostgreSQL is configured, we will introduce a JavaScript library, Sequalize, to provide a developer-friendly interface for the data models and queries.\n\n### 1. Install Sequelize and Postgres Libraries\nUse the package manager to add the sequeilze and postgres client libraries.\n\n<div class=\"filename\">command line</div>\n\n```\n$ npm install --save sequelize sequelize-cli pg pg-hstore \n```\n\n`sequelize` and `sequelize-cli` are the developer interface, and contain the functions and classes we will be using primarily. `pg` and `pg-hstore` are lower-level client drivers between Node and Postgres. These libraries are required for runtime in production, so use the `--save` flag to add them as dependencies in `package.json`.\n\n### 2. Initialize Sequelize\n1. Create a file in the root of the project named `.sequelizerc` with the following contents:\n\n<div class=\"filename\">.sequelizerc</div>\n\n```javascript\nconst path = require('path');\n\nmodule.exports = {\n 'config': path.resolve('config', 'sequelize.js'),\n 'models-path': path.resolve('models'),\n 'migrations-path': path.resolve('db', 'migrations'),\n 'seeders-path': path.resolve('db', 'seeds'),\n};\n```\n\n2. Run the `sequelize-cli init` command for the library to add the required boilerplate.\n\n<div class=\"filename\">command line</div>\n\n```\n$ npx sequelize-cli init\n```\n\nBased on the configuration within `.sequelizerc`, the command creates following folders:\n* config/sequelize.js -- the Sequelize config file which tells CLI how to connect with database\n* models -- the directory for the data models for your project\n* db/migrations -- the directory for the database migration files\n* db/seeds -- the directory for the database seed files\n\n### 3. Configure database credentials\nFor the Node.js application to connect to the Postgres server, it must be configured with the a) server's address, b) user name, and c) user password. The file `./config/sequelize.js`, contains the database connection configuration for three environments -- `development`, `test`, and `production`. set the username, password, database, and dialect.\n\nChange the username and password for the `development` connection to credentials of your Postgres user. Change the database to the name of the database your application should use. Sequelize will create the database if one by the name specified does not already exist. Change the dialect to `postgres`. \n\nThe `test` and `production` configurations will need to be corrected before running database transactions in those environment.\n\n<div class=\"filename\">config/sequelize.js</div>\n\n```\nmodule.exports = {\n \"development\": {\n \"username\": \"root\", // Change this\n \"password\": null, // Change this\n \"database\": \"my-service\", // Change this\n \"host\": \"127.0.0.1\",\n \"dialect\": \"postgres\" // Change this\n },\n ...\n}\n```\n\nAs shown in the code example, export the configuration object using `module.exports = ` at the beginning of the file.\n\n### 4. Create the database\nCreate the application's database.\n\n<div class=\"filename\">command line</div>\n\n```\n$ npx sequelize db:create\n```\n\n### Resources\n`sequlize-cli` documentation: [https://github.com/sequelize/cli](https://github.com/sequelize/cli)\n\nWhat is an ORM?: [https://stackoverflow.com/a/1279678/18752242](https://stackoverflow.com/a/1279678/18752242)",
"sectionType": "section",
"sequence": 4,
"parentSection": {
"parentSectionPart": 2,
"parentSectionType": "chapter",
"parentSectionSequence": 6
}
},
{
"title": "Using Sequelize",
"slug": "using-sequelize",
"content": "## Using Sequelize in a Node.js Application\nSequelize is an ORM -- an Object Relational Mapping library. The benefit of an ORM is that it abstracts SQL query dialect into application language. It also converts the response of any query into application level data types such as arrays or custom objects. Sequelize and many other ORM libraries call these custom objects \"models.\" \n\nA database table is a collection of objects with specified data attributes. Viewed as a table, there is a row for each specific object and columns for attributes an object can have. A ficticious \"posts\" that contains a collection of resources with the attributes of title, content, and created_at.\n\n```\npostgres=# select * from posts;\nid | title | content | publishDate \n---+---------------------+---------------------------------+------------------------\n1 | My First Post | Hello World! | 2022-04-03 12:00:00-06\n2 | How to JavaScript | Objects and functions. | 2022-04-04 12:00:00-06\n3 | SOLID Breakfast | The principles of SOLID design. | 2022-04-05 12:00:00-06\n(3 rows)\n```\n\nIn an ORM, this structure is made accessible in the programming language, such as JavaScript's class objects. These models are an abstraction that represents a table in the database.\n\nWe can use `sequelize-cli` to generate both the database tables and JavaScript classes. \n\n### 1. Generate a `posts` migration and JS classes using the `model:generate` command.\n\n<div class=\"filename\">command line</div>\n\n```\n$ npx sequelize model:generate --name Post --attributes title:string,content:text,publishDate:date\n```\nThis creates two files\n* models/post.js\n* db/migrations/[timestamp]-create-post.js\n\n### 2. Run the migration.\nMigration files are used to keep track of changes made to a database. Migrations are used to track creating tables, adding and removing columns to existing tables, and other operations. Migration files record how to transition the database to a new state, and how to rollback the changes to get back to the older state.\n\nDatabase migration files are like a version control system for the application database, and provide replayable changes that keep the variety of development databases, test databases, and production databases in sync.\n\nLooking at the migration file generated by `model:generate`, we can see that Sequelize added two attributes `createdAt` and `updatedAt`. This is convention, and the values will be set and kept up to date by the Sequelize engine.\n\nRun the migration command to create the `posts` table in the database.\n\n<div class=\"filename\">command line</div>\n\n```\n$ npx sequelize db:migrate\n```\n\nThis command executes the following steps:\n* Ensures a table called SequelizeMeta is in database. This table is used to record which migrations have run on the database.\n* Runs any migration files which haven't run yet. This is possible by checking SequelizeMeta table.\n\nIn this case, the `create-post` migration file will be executed against the database resulting in a new `posts` table being created.\n\n### 3. Seed the database\nTo \"seed\" the database is to programmatically insert values into the database -- no user interaction required. This is useful for transferring a known data set into the database or populating tables with dummy data for development.\n\n1. Track development seeds within database.\nUnlike migrations, database seeding events are not stored anywhere by default. This means every time the `db:seed:all` command is run, the database will be re-seeded with previously run seeds. To change from the default behavior, add the configuration `\"seederStorage\": \"sequelize\"` to the development object of `config/sequelize.js`.\n\nThis will save to the database which seeds have been run, allowing for use of the developer friendly `db:seed:all`.\n\n<div class=\"filename\">config/sequelize.js</div>\n\n```\nmodule.exports = {\n \"development\": {\n ...,\n \"seederStorage\": \"sequelize\"\n },\n ...\n}\n```\n\n2. Generate a new seed file\nUse the `seed:generate` command provided by `sequelize-cli` to generate a seed file for the Post model.\n\n<div class=\"filename\">command line</div>\n\n```\n$ npx sequelize seed:generate --name first-posts\n```\n\nThis command creates a file, `db/seeds/[timestamp]-first-posts.js`. Like a migration, the seed file implements an `up/down` interface. The `up` command specifies what actions should be performed to seed the database. The `down` function should specify how to undo the actions.\n\n3. Define a few post objects in an array:\n\n<div class=\"filename\">db/seeds/[timestamp]-first-posts.js</div>\n\n```javascript\nconst posts = [{\n title: 'Hello World',\n content: 'This is the first post!',\n publishDate: new Date('2022-01-01'),\n createdAt: new Date(),\n updatedAt: new Date()\n}, {\n title: 'Lorem Ipsum',\n content: `\n Lorem Ipsum is simply dummy text of the printing and typesetting industry.\n Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,\n when an unknown printer took a galley of type and scrambled it to make a type specimen book.\n Contrary to popular belief, Lorem Ipsum is not simply random text.\n It has roots in a piece of Latin literature from 45 BC.\n `,\n publishDate: new Date('2022-01-02'),\n createdAt: new Date(),\n updatedAt: new Date()\n}];\n```\n\n4. Define the `up` and `down` methods\nIn the `up` method, use the provided `queryInterface` class to bulk insert the posts into the database.\n\n<div class=\"filename\">db/seeds/[timestamp]-first-posts.js</div>\n\n```javascript\nasync up (queryInterface, Sequelize) {\n await queryInterface.bulkInsert('Posts', posts, {});\n},\n```\n\nIn the `down` method, perform the reverse action of the `up` method by deleting the posts. To use Sequelize's `Op` (short for \"operation\") library, import it at the top of the file.\n\n<div class=\"filename\">db/seeds/[timestamp]-first-posts.js</div>\n\n```javascript\nconst { Op } = require(\"sequelize\");\n\nconst posts = [...];\n\nmodule.exports = {\n async up (queryInterface, Sequelize) { ... },\n\n async down (queryInterface, Sequelize) {\n await queryInterface.bulkDelete('Posts', {\n title: {\n [Op.in]: posts.map((post) => post.title)\n }\n }, {});\n }\n}\n```\n\nThis `bulkDelete` query generates the following SQL:\n```\nDELETE FROM \"Posts\" WHERE posts.title IN [\"Hello World\", \"Lorem Ipsum\"];\n```\n\n3. Seed the database\n\n<div class=\"filename\">command line</div>\n\n```\n$ npx sequelize-cli db:seed:all\n```\nThis command inserts the records into the database.\n\n### 4. Display database records\nThe purpose of a database is to keep data organized. The purpose of keeping the data around is for human end-users to view and manipulate. To get the data viewable by the user, we will provide a webpage that lists the data. As part of handling the webpage request, we will query the database for the records, and supply the records as template variables.\n\n1. Create the route.\nCreate a route `/posts` in `index.js`.\n\n<div class=\"filename\">index.js</div>\n\n```javascript\napp.get('/posts', (request, response) => {\n response.render('posts', {\n posts: []\n });\n});\n```\n\n2. Create the template\nCreate a new file, `app/views/posts.liquid` with the following code:\n\n<div class=\"filename\">views/posts.liquid</div>\n\n```html\n{% layout 'layouts/default-html.liquid' %}\n{% block content %}\n<div>\n <h1>Posts</h1>\n\n {% for post in posts %}\n <h2>{{ post.title }}</h2>\n <p><i>{{ post['publishDate'] }}</i></p>\n <p>{{ post['content'] }}</p>\n {% else %}\n <p><i>There are no posts to display.</i></p>\n {% endfor %}\n</div>\n{% endblock %}\n```\n\nNavigate to `localhost:3000/posts`. Because the `posts` template variable is hard-coded to be an empty array, you should see a page that says \"There are no posts to display.\"\n\n3. Query the database for posts\nSequelize as an ORM provides JavaScript classes as abstraction over the SQL query language. The `Post` class found in `models/post.js` is such a class. We will import the class into `index.js` and use the `.findAll()` method to populate the `posts` template variable.\n\n<div class=\"filename\">index.js</div>\n\n```javascript\nconst { Post } = require('./models');\n\napp.get('/posts', async function(request, response) {\n response.render('posts', {\n posts: await Post.findAll()\n });\n});\n```\n\nWith the addition of the asynchronous method `Post.findAll()`, we must also label the route handling function as `async`. Notice the addtion of the keyword `async` before the function defintion.\n\nPlace the require statement at near the top of the file with the other `require` statements. Place the route near the other routes definitions.\n\nRefreshing the `/posts` web page now shows the two posts seeded in the database.\n\n4. Add a link to Posts on the homepage.\nShow off the database! Add a navigation link to the `views/index.liquid`.\n\n<div class=\"filename\">index.liquid</div>\n\n```html\n<li><a href=\"/posts\">Posts</a></li>\n```\n\n### Resources\n\nSequelize model basics: [https://sequelize.org/docs/v6/core-concepts/model-basics](https://sequelize.org/docs/v6/core-concepts/model-basics)\n\nSequelize Seeds: [https://sequelize.org/docs/v6/other-topics/migrations](https://sequelize.org/docs/v6/other-topics/migrations/#creating-the-first-seed)\n\nSequelize Query Interface: [https://sequelize.org/docs/v6/other-topics/query-interface/](https://sequelize.org/docs/v6/other-topics/query-interface/)\n\nSequelize `QueryInterface` API : [https://sequelize.org/api/v6/class/src/dialects/abstract/query-interface.js](https://sequelize.org/api/v6/class/src/dialects/abstract/query-interface.js~queryinterface)",
"sectionType": "section",
"sequence": 5,
"parentSection": {
"parentSectionPart": 2,
"parentSectionType": "chapter",
"parentSectionSequence": 6
}
},
{
"title": "Deployed Database",
"slug": "deployed-database",
"content": "## Database in the Deployed Environment\nEach environment -- development, test, production, etc -- will likely use a different PostgreSQL database server. This means the Node.js application will need to be configured with a database URL and user credentials at a per environment specification. Sequelize's `config` file provides the location to specify these differences.\n\nThe platform we will be using to host the PostgreSQL server is Heroku Postgres, an add-on provided by Heroku. There is a free tier with paid plans available to increase data capacity and concurrency as the application storage and/or traffic grows. Heroku Postgres configures the server URL and user credentials, and provides these values via an environment variable, `DATABASE_URL`.\n\n### 1. Add the Heroku Postgres add-on\n1. From the command-line interface, use the `heroku addons:create` command to add the Heoku Postgres add-on, hobby-dev tier.\n\n<div class=\"filename\">command line</div>\n\n```\n$ heroku addons:create heroku-postgresql:hobby-dev\n```\n\n2. Use the `DATABASE_URL` environment variable in production\nWithin the `config/sequelize.js` **production** object, set the keys `use_environment_variable` and `ssl.rejectUnauthorized`. Remove the unneeded piecewise credentials; `DATABASE_URL` contains user and database location information. The following code snippet includes the full `production` object.\n\n<div class=\"filename\">config/sequelize.js</div>\n\n```\n\"production\": {\n \"use_env_variable\": \"DATABASE_URL\",\n \"dialect\": \"postgres\",\n \"dialectOptions\": {\n \"ssl\": {\n \"rejectUnauthorized\": false\n }\n }\n}\n```\n\n3. Ensure the NODE_ENV environment variable is set on Heroku server.\n\n<div class=\"filename\">command line</div>\n\n```\n$ heroku config:set NODE_ENV=production\n```\n\n### 2. Run the application\n1. Commit and push the new changes to Heroku\n\n<div class=\"filename\">command line</div>\n\n```\n$ git add .\n$ git commit -m 'Use Heroku Postgres'\n$ git push heroku HEAD\n```\n\n2. Run the database migration on Heroku\nUse the `heroku run` command to execute the Sequelize CLI commands in Heroku's server environment.\n\n<div class=\"filename\">command line</div>\n\n```\n$ heroku run npx sequelize db:migrate --env production\n```\n\n3. Seed the database\n\n<div class=\"filename\">command line</div>\n\n```\n$ heroku run npx sequelize db:seed:all\n```\n\n4. View the app\nIssue the command `heroku open` to open the deployed application. Navigate to the `/posts` route to see the seeded posts.\n\n<div class=\"filename\">command line</div>\n\n```\n$ heroku open\n```\n\nThat's a deployed database! This simple tool is the backbone of the internet. Now that we have a database live on the interconnected web, we are able to provide our users with experiences on our web apps that can be customized on a per user basis.\n\nWe will be looking at saving user generated information in upcoming sections.\n\n### Resources\n\nHeroku Postgres: [https://devcenter.heroku.com/articles/heroku-postgresql](https://devcenter.heroku.com/articles/heroku-postgresql)\n\nSequelize Heroku Postgres Settings: [https://github.com/sequelize/sequelize/issues/956](https://github.com/sequelize/sequelize/issues/956#issuecomment-778149933)\n\nDeploy Sequelize to Heroku: [https://anjelicaa.medium.com](https://anjelicaa.medium.com/deploying-a-node-js-postgres-sequelize-app-to-heroku-da3dc9de07cd)\n",
"sectionType": "section",
"sequence": 6,
"parentSection": {
"parentSectionPart": 2,
"parentSectionType": "chapter",
"parentSectionSequence": 6
}
},
{
"title": "User Interaction Project",
"slug": "user-interaction-project",
"content": "## Capturing User Information\r\nThe strength of a database comes into play when the developer uses it to capture user interaction within the web application. This saved information can then be displayed to the user to demonstrate the applications interactivity.\r\n\r\n### Click Tracker Application\r\nThis feature will allow any user to click a button and counter will increment. This counter increments over time as users click the button. If you think about it, users from anywhere on the globe can log into this application, and click this button. Pretty cool.\r\n\r\nThis feature will require:\r\n* a button\r\n* text displaying how many times the button has been clicked\r\n\r\n### 1. Decide the data model\r\nThis feature will require that we persist the total number of times a button has been clicked in a database. To accomplish this, we will create a database table, `Clicks`.\r\n```\r\nClicks\r\n-----------+-------------+\r\nuser | VARCHAR\r\ncreatedAt | DATE\r\nupdatedAt | DATE\r\n```\r\n\r\nTo find how many times the button has been clicked, a SQL `COUNT(*)` command can be used. This data model has the additional benefit of saving user information with the click event. This will be aided by the authentication system, and allows for a follow-up feature of displaying how many times a particular user has clicked the button.\r\n\r\nGenerate a model and migration using Sequelize's `model:generate` command, and migrate the database with `db:migrate`.\r\n\r\n<div class=\"filename\">command line</div>\r\n\r\n```\r\n$ npx sequelize model:generate --name Click --attributes user:string\r\n$ npx sequelize db:migrate\r\n```\r\n\r\n### 2. Create the web page route\r\nWithin `index.js`, create a route, `/click-tracker`. This route should render a page `click-tracker.liquid`.\r\n\r\nThe number of times the button has been clicked in total will be saved in a database, and fetched at the initial user request. The liquid-HTML template will be rendered with this number. Hard-code the value to 10 for now.\r\n\r\n1. Import the `Click` class from Sequelize's model directory.\r\n\r\n<div class=\"filename\">index.js</div>\r\n\r\n```javascript\r\nconst { Post, Click } = require('./app/models');\r\n```\r\n\r\n2. Create the route.\r\nThe route handler must be labeled async to be able to use the asynchronous `await Click.count()`. Send the result of `Click.count()` to the view as the template variable `timesClicked`.\r\n\r\n<div class=\"filename\">index.js</div>\r\n\r\n```javascript\r\napp.get('/click-tracker', async function (request, response) {\r\n response.render('click-tracker', {\r\n timesClicked: await Click.count()\r\n });\r\n});\r\n```\r\n\r\n3. Create the webpage.\r\nA view template named `click-tracker.liquid` must be created.\r\n\r\n<div class=\"filename\">views/click-tracker.liquid</div>\r\n\r\n```html\r\n{% layout 'layouts/default-html.liquid' %}\r\n{% block content %}\r\n<h1>Click Tracker</h1>\r\n<button id=\"click-me\">Click Me!</button>\r\n<p>This button has been clicked <span id=\"times-clicked\">{{ timesClicked }}</span> times.</p>\r\n{% endblock %}\r\n```\r\n\r\nYou should now be able to start the server, navigate to `https://localhost:3000/click-tracker`, and see the desired initial page.\r\n\r\n### 3. Handle user interaction\r\nAt this point, nothing happens if a user clicks the on-screen button. Let's change this by adding a JavaScript click event listener on the button. Add a `<script>` tag within the `content` block.\r\n\r\n<div class=\"filename\">views/click-tracker.liquid</div>\r\n\r\n```html\r\n<script>\r\n const button = document.getElementById('click-me');\r\n button.addEventListener('click', function(event) {\r\n console.log('Button clicked!');\r\n });\r\n</script>\r\n```\r\n\r\n### 4. Make API request on click\r\nFor the moment, the click handling function only prints out `'Button clicked!'`. The desired functionality is for the button click to initiate a request to the webserver. The webserver will receive the request and add a `Click` database record.\r\n\r\nFrom the front-end, we will use the Fetch API to make and handle the network request. The Fetch API provides a JavaScript interface for fetching resources and interacting with the HTTP pipeline. Within a browser (e.g. Chrome), a global `fetch()` method provides an easy-to-use way to fetch resources asynchronously across the network.\r\n\r\nThe `fetch()` method returns a Promise. A JavaScript Pro\r\n\r\nReplace the `console.log` within the click handler with a `fetch` call.\r\n\r\n<div class=\"filename\">views/click-tracker.liquid</div>\r\n\r\n```html\r\n<script>\r\n const button = document.getElementById('click-me');\r\n button.addEventListener('click', function(event) {\r\n fetch('/api/clicks', { method: 'POST' })\r\n .then((response) => {\r\n response.ok ? console.log('Click Added') : console.log('Error Occured');\r\n });\r\n });\r\n</script>\r\n```\r\nThe `response.ok` is a utility property on the `Response` object returned by `fetch`. A response with an HTTP status code 200-299 has and `ok` value of `true`.\r\n\r\n### 5. Create the API route\r\nIn line with RESTful standards, we will make a route that accepts a `POST` request to `/clicks` to create a click resource. The the `POST` request is successful, we will return to the user the new total number of clicks in the database.\r\n\r\n1. Create the POST `/clicks` route\r\n\r\n<div class=\"filename\">indes.js</div>\r\n\r\n```javascript\r\napp.post('/api/clicks', async function(request, response) {\r\n const user = request.oidc.user ? request.oidc.user.email : null;\r\n await Click.create({ user: user });\r\n response.json({ timesClicked: await Click.count() });\r\n});\r\n```\r\n\r\n### 6. Handle an error using Express middleware\r\nDue to validations and user input errors, creating database records is a process that is expectedly error prone. Click Tracker deals with a relatively small model with no truly custom user input, yet it is good practice to handle where known errors may arise and deliver useful information to the front-end application and user.\r\n\r\nThe Express way to handle errors is to use its middleware framework. We have already used Express' middleware in implementing the `auth()` functionality. The middleware framework is a pipeline of functions that have access to the `request` and `response` objects. A given middleware can execute any code and make changes to the `request` and `response` objects. When it is done with its computation, it must end the request/response cycle or call the `next` middleware function in the pipeline.\r\n\r\nIn this way, every route that is defined -- e.g. `GET /hello-world` -- is part of the middleware pipeline. The routes created thus far end the request/response cycle by not calling a `next` middleware. In fact, because `next` has not been needed, I have left this variable out of the route handler definitions. An Express route handler has the following signature:\r\n\r\n<div class=\"filename\">pseudocode</div>\r\n\r\n```javascript\r\nconst routeHandler = function(request, response, next) { ... };\r\napp.get('/path', routeHandler);\r\n```\r\n\r\nA middleware handler has the similar signature:\r\n\r\n<div class=\"filename\">pseudocode</div>\r\n\r\n```javascript\r\nconst middleware = function(request, response, next) { ... };\r\napp.use(middlewareHandler);\r\n```\r\n\r\nError handling middleware has a slightly differing signature; the first parameter is a JavaScript error object.\r\n\r\n<div class=\"filename\">pseudocode</div>\r\n\r\n```javascript\r\nconst errorHandlingMiddleware = function(error, request, response, next) { ... };\r\napp.use(errorHandlingMiddleware);\r\n```\r\nThe application knows to use the error handling middleware if `next` is invoked with an error object.\r\n\r\n1. Create the error handling middleware.\r\nAs will all middleware, Express will invoke the functions in the order they are applied to the application with `app.use()`, top to bottom. As such, `app.use` this middleware below the route definitions within `index.js`.\r\n\r\n<div class=\"filename\">index.js</div>\r\n\r\n```javascript\r\napp.use(function (error, request, response, next) {\r\n if (!error.apiError) {\r\n return next(error, request, response, next);\r\n }\r\n response.status(error.statusCode);\r\n response.json({ message: error.message });\r\n});\r\n\r\n```\r\nThis code checks for the existance of the a property `apiError` on the `error` parameter. If it is not present, the function passes the error to the next error handling middleware. If the property is present, the status of the response is set to the statusCode of the error, and a JSON response is returned with the error's message.\r\n\r\nAn important aspect of this code is that it returns a JSON response. Express' default error handler returns an HTML response. For `pd-service`, we will standardize this behavior and return JSON in case of error.\r\n\r\n2. Invoke the error handler in case of application error.\r\nWith the error handler is in place, the route handler must be changed to pass any errors to the error handling middleware. The third parameter, `next`, should be added to the handler's function definition. It has always been passed in at runtime, but because it was unnecessary, it hasn't been added to the code until now.\r\n\r\n`Click.create` will throw an error if the create is unsuccessful. Wrap this function call in a `try/catch` block. If an error is caught, set the properties on it the custom error handling middleware is expecting -- `apiError` and `statusCode` -- and invoke the `next` middleware the error.\r\n\r\n<div class=\"filename\">index.js</div>\r\n\r\n```javascript\r\napp.post('/api/clicks', async function(request, response, next) {\r\n const user = request.oidc.user ? request.oidc.user.email : null;\r\n try {\r\n await Click.create({ user: user });\r\n response.json({ timesClicked: await Click.count() });\r\n } catch (e) {\r\n e.apiError = true;\r\n e.statusCode = 422;\r\n next(e);\r\n }\r\n});\r\n```\r\n\r\nWith the last line -- `next(e)` -- the request/response cycle is moved to the error handling middleware pipeline.\r\n\r\n### 7. Handle the API response\r\nThe sequence of events currently programmed is the following:\r\n* User clicks the button\r\n* A `fetch` request is made to the `/clicks` route\r\n* The route processes the request and returns a JSON response\r\n\r\nA response handler must be written within the front-end JavaScript to process the response.\r\n\r\nThe `fetch` call resolves to a `Response` interface that represents the response to a request. The `json()` method on this interface returns a promise of the result of parsing the response body into JSON. We'll want to access the `timesClicked` property we set on the response body.\r\n\r\n<div class=\"filename\">views/click-tracker.liquid</div>\r\n\r\n```html\r\n<script>\r\n const button = document.getElementById('click-me');\r\n button.addEventListener('click', function(event) {\r\n fetch('/api/clicks', { method: 'POST' })\r\n .then((response) => {\r\n if (!response.ok) return;\r\n \r\n response.json()\r\n .then((data) => {\r\n document.getElementById('times-clicked').innerHTML = data.timesClicked;\r\n });\r\n });\r\n });\r\n</script>\r\n```\r\nThe script makes use of a preset `<span id=\"times-clicked\">`, and replaces the value that present there with the more recent count of clicks.\r\n\r\nYou should now be able to click the button multiple times and see the number on screen increment by one each time. If you refresh the page, the number will remain at the last seen value.\r\n\r\n### 8. Handle the API error response\r\nIt is good practice to inform the user of an application error. It's wise to consider whether the user can be helped by the error. For example, it's prudent to show the user if the error is due to an input validation error; the user can change their input and correct the problem. If the error is due to an obscure error the user cannot correct, such as invalid database credentials failing authentication, it is more appropriate to show the user a generic error or none at all.\r\n\r\nThe case of the Click Tracker application coming into an error state fits into the latter distinction. The plan is to place an error message within the HTML. It will be hidden by default, but when an error response is received, it will be displayed. Whenever a new request is initalized -- when the user re-clicks the button -- the error message will be re-hidden while the new `fetch` request is sent and allowed to return successfully or not.\r\n\r\n1. Add the HTML/CSS for error handling.\r\nFor this, we will need to add the error message element, and set it to be hidden by default. Add the new element after the `click-me` button. Add the style tags within the `content` block.\r\n\r\n<div class=\"filename\">views/click-tracker.liquid</div>\r\n\r\n```html\r\n<span id=\"error\" class=\"hidden\">Oops, something happened.</span>\r\n\r\n<style>\r\n .hidden {\r\n display: none;\r\n }\r\n\r\n #error {\r\n color: red;\r\n }\r\n</style>\r\n```\r\n\r\n2. Add error handling JavaScript\r\nWhen an error response is encountered, remove the `hidden` class on the `#error` element to remove the `display: none` attribute. In the case of resubmitting the button click, hide the element again by re-adding the `hidden` class.\r\n\r\n<div class=\"filename\">views/click-tracker.liquid</div>\r\n\r\n```html\r\n<script>\r\n const button = document.getElementById('click-me');\r\n button.addEventListener('click', function(event) {\r\n document.getElementById('error').classList.add('hidden');\r\n\r\n fetch('/api/clicks', { method: 'POST' })\r\n .then((response) => {\r\n if (!response.ok) {\r\n document.getElementById('error').classList.remove('hidden');\r\n return;\r\n };\r\n\r\n response.json()\r\n .then((data) => {\r\n document.getElementById('times-clicked').innerHTML = data.timesClicked;\r\n });\r\n });\r\n });\r\n</script>\r\n```\r\n\r\nTo test the error handling, you can force the API to return an error response.\r\n\r\n<div class=\"filename\">index.js</div>\r\n\r\n```javascript\r\napp.post('/api/clicks', async function(request, response, next) {\r\n // const user = request.oidc.user ? request.oidc.user.email : null;\r\n // try {\r\n // await Click.create({ user: user });\r\n // response.json({ timesClicked: await Click.count() });\r\n // } catch (e) {\r\n // e.apiError = true;\r\n // e.statusCode = 422;\r\n // next(e);\r\n // }\r\n\r\n const e = new Error();\r\n e.apiError = true;\r\n e.statusCode = 500;\r\n next(e);\r\n});\r\n\r\n```\r\n\r\nBe sure to revert this intermediate step for the application to function as planned long-term.\r\n\r\n### 9. Add a homepage link\r\nAdd the Click Tracker app to the list of pages on the homepage.\r\n\r\n<div class=\"filename\">views/index.liquid</div>\r\n\r\n```html\r\n<li><a href=\"/click-tracker\">Click Tracker</a></li>\r\n```\r\n\r\n### 10. Commit and deploy\r\n1. Commit the repository\r\nGit commit the new changes and deploy to Heroku to see the results in a deployed environment.\r\n\r\n<div class=\"filename\">command line</div>\r\n\r\n```\r\n$ git add .\r\n$ git commit -m 'Add Click Tracker'\r\n$ git push heroku HEAD\r\n```\r\n\r\n2. Migrate the production database\r\nThere is now a new table the application expects to be in the database. A database migration must be run on the Heroku Postgres instance to create this table.\r\n\r\n<div class=\"filename\">command line</div>\r\n\r\n```\r\n$ heroku run sequelize db:migrate\r\n```\r\n\r\n3. Run the deployed application\r\n\r\n<div class=\"filename\">command line</div>\r\n\r\n```\r\n$ heroku open\r\n```\r\n\r\nNavigate to `/click-tracker` directly or via the homepage link, and verify the incrementing Click Tracker.\r\n\r\n### Resources\r\n\r\nWhat is Web 2.0?: [https://www.znetlive.com/blog/web-2-0/](https://www.znetlive.com/blog/web-2-0/)\r\n\r\nUsing the Fetch API: [https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch)\r\n\r\nExpress Error Handling: [http://expressjs.com/en/guide/error-handling.html](http://expressjs.com/en/guide/error-handling.html)\r\n\r\nJavaScript Promises: [https://nodejs.dev/learn/understanding-javascript-promises](https://nodejs.dev/learn/understanding-javascript-promises#chaining-promises)",
"sectionType": "chapter",
"sequence": 7,
"parentSection": {
"parentSectionPart": 2,
"parentSectionType": "part",
"parentSectionSequence": 2
}
}
]
},
{
"title": "Features",
"slug": "features",
"content": "## README.md\n\n**Features** is an exploration of the range of applications\ncan be built from the tooling of the Basics. Features contains walkthroughs\nfor extending the functionality of the app.\n\nFeatures are standalone front-end or full-stack modules that can be completed in any order.",
"sectionType": "part",
"sequence": 3,
"parentSectionId": null,
"childSections": [
{
"title": "Book Catalog",
"slug": "Book-Catalog",
"content": "## Book Catalog\n\nA book catalog will list book, text resources. Each book will have a title, author, description, and blurb for display purposes. Each book will also have a slug which will be used for routing purposes. Each book will be accesible at a route `/books/<book-slug>`, where `book-slug` is dynamic per book.\n\nEach book will be composed of parts. Each part will have futher sub-parts. This resembles the structure of book, part, chapter, chapter section, etc. while allowing the developer (and authors) to add however many nested sub-parts. To accomplish the relation of part to child-part, a parent key will be recorded on the child part. A list of all child parts for a given book part can be found by querying for all resourcess with a parent key matching the primary key of the interested book part.\n\n\n",
"sectionType": "chapter",
"sequence": 1,
"parentSection": {
"parentSectionPart": 3,
"parentSectionType": "part",
"parentSectionSequence": 3
}
},
{
"title": "Book Resource",
"slug": "book-resource",
"content": "## The Book Resource\n\n### 1. Create the Book Data Model\n1. Generate the book model\n```\nnpx sequelize model:generate --name Book --attributes title:string,author:string,description:text,blurb:text,slug:string\n```\n\n2. Seed the database\n```\n npx sequelize seed:generate --name first-book\n ```\n\n ```javascript\nconst { Op } = require(\"sequelize\");\nconst { Book } = require('../../app/models');\n\nconst books = [{\n title: 'Build a SmartSite',\n author: 'Popular Demand',\n description: 'A step-by-step guide to creating a dynamic website using Node.js.',\n blurb: 'A step-by-step guide to creating a dynamic website using Node.js.',\n slug: 'build-a-smartsite',\n createdAt: new Date(),\n updatedAt: new Date()\n}];\n\nmodule.exports = {\n async up (queryInterface, Sequelize) {\n await queryInterface.bulkInsert('Books', books, {});\n },\n\n async down (queryInterface, Sequelize) {\n await queryInterface.bulkDelete('Books', {\n title: {\n [Op.eq]: books[0].title\n }\n }, {});\n }\n};\n ```\n\n ```\nnpx sequelize-cli db:seed:all\n ```\n\n### 2. All Books index page\n\n1. Create Books index route\n```\nconst { Post, Click, Book } = require('./app/models');\napp.get('/books', async function(request, response) {\n const books = await Book.findAll();\n response.render('books/index', { books });\n});\n```\n\n2. Create Books index view\n```\n{% layout 'layouts/default-html.liquid' %}\n{% block content %}\n<div>\n <h1>Books</h1>\n\n {% for book in books %}\n <h2>{{ book.title }}</h2>\n <p><i>{{ book.author }}</i></p>\n <p>{{ book.blurb }}</p>\n {% else %}\n <p><i>There are no books to display.</i></p>\n {% endfor %}\n</div>\n{% endblock %}\n```\n\n```\n<li><a href=\"/books\">Books</a></li>\n```\n\nBooks page is available at `localhost:3000/books`.\n\n### 3. One Book show page\n\n1. Create show route for single Book\n```\napp.get('/books/:slug', async function(request, response, next) {\n const book = await Book.findOne({ where: { slug: request.params.slug }});\n if (book == null) {\n // handling if a book is not found is covered next\n }\n response.render('books/show', { book });\n});\n```\n\n2. Create Book show view\n```\n{% layout 'layouts/default-html.liquid' %}\n{% block content %}\n<div>\n <h1>{{ book.title }}</h1>\n <p><i>{{ book.author }}</i></p>\n <p>{{ book.description }}</p>\n</div>\n{% endblock %}\n```\n\n3. Link to the book show page\n```\n<a href=\"/books/{{ book.slug }}\">\n <span class=\"book-panel\">\n <h2>{{ book.title }}</h2>\n <p><i>{{ book.author }}</i></p>\n <p>{{ book.blurb }}</p>\n </span>\n</a>\n```\n\n### 4. 404 - Not Found Route\n1. Handle if a book is not found\nSet the response status to `404`, and forward the request to the next middleware.\n```\napp.get('/books/:slug', async function(request, response, next) {\n const book = await Book.findOne({ where: { slug: request.params.slug }});\n if (book == null) {\n response.status(404);\n next();\n return;\n }\n response.render('books/show', { book });\n});\n```\n\n2. Create a 404 middleware\n```\napp.use(function(request, response, next) {\n if (response.statusCode === 404) {\n response.render('404');\n return;\n }\n next();\n});\n```\n\n3. Create a 404 view template\n```\n{% layout 'layouts/default-html.liquid' %}\n{% block content %}\n<div>\n <h1>Not Found</h1>\n\n <p>\n The requested resource was not found.\n </p>\n</div>\n{% endblock %}\n```\n\n### 5. Add Breadcrumbs\nAdd breadcrumbs to the show page.\n```\n<div class=\"breadcrumbs\">\n <a href=\"/books\">Books</a> > {{ book.title }}\n</div>\n```",
"sectionType": "section",
"sequence": 1,
"parentSection": {
"parentSectionPart": 3,
"parentSectionType": "chapter",
"parentSectionSequence": 1
}
},
{
"title": "Book Part Resource",
"slug": "book-part-resource",
"content": "## The Book Divison Resource\n\nBookPart\n- title\n- slug\n- content\n- sectionType\n- sequence\n- parent_part\n- book\n\n### 1. Create the Data Model\n\n1. Generate model\n```\nnpx sequelize model:generate --name BookSection --attributes title:string,slug:string,content:text,sectionType:string,sequence:integer,bookId:integer\n```\n\n2. Customize generated files\nCustomize the generated model and migration files to refect the Book to Book Section one-to-many relatonship.\nmodel file:\n\n```\nconst {\n Model\n} = require('sequelize');\n\nmodule.exports = (sequelize, DataTypes) => {\n class BookSection extends Model {\n static associate(models) {\n BookSection.belongsTo(models.Book, { foreignKey: 'bookId' });\n }\n }\n\n BookSection.init({\n title: DataTypes.STRING,\n slug: DataTypes.STRING,\n content: DataTypes.TEXT,\n sectionType: DataTypes.STRING,\n sequence: DataTypes.INTEGER,\n bookId: {\n type: DataTypes.INTEGER,\n references: {\n model: 'Books',\n key: 'id'\n }\n },\n parentSectionId: {\n type: DataTypes.INTEGER,\n references: {\n model: 'BookSections',\n key: 'id'\n }\n }\n }, {\n sequelize,\n modelName: 'BookSection',\n });\n\n return BookSection;\n};\n```\n\nmigration, changes to slug and bookId:\n```\nmodule.exports = {\n async up(queryInterface, Sequelize) {\n await queryInterface.createTable('BookSections', {\n id: {\n allowNull: false,\n autoIncrement: true,\n primaryKey: true,\n type: Sequelize.INTEGER\n },\n title: {\n type: Sequelize.STRING\n },\n slug: {\n type: Sequelize.STRING,\n allowNull: false\n },\n content: {\n type: Sequelize.TEXT\n },\n sectionType: {\n type: Sequelize.STRING\n },\n sequence: {\n type: Sequelize.INTEGER\n },\n bookId: {\n type: Sequelize.INTEGER,\n references: {\n model: {\n tableName: 'Books'\n },\n key: 'id'\n },\n allowNull: false\n },\n createdAt: {\n allowNull: false,\n type: Sequelize.DATE\n },\n updatedAt: {\n allowNull: false,\n type: Sequelize.DATE\n }\n });\n },\n async down(queryInterface, Sequelize) {\n await queryInterface.dropTable('BookSections');\n }\n};\n```\n\n```\n$ npx sequelize db:migrate\n```\n\n### 3. Add Parent Section Reference\n1. generate migration\n```\n$ npx sequelize migration:generate --name add-parent-book-section\n```\n\n2. Fill in migration\n```\nmodule.exports = {\n async up (queryInterface, Sequelize) {\n return await queryInterface.addColumn('BookSections', 'parentSectionId', {\n type: Sequelize.INTEGER,\n references: {\n model: {\n tableName: 'BookSections'\n },\n key: 'id'\n }\n })\n },\n\n async down (queryInterface, Sequelize) {\n return queryInterface.removeColumn('BookSections', 'parentSectionId');\n }\n};\n```\n\n3. Run migration\n```\n$ npx sequelize db:migrate\n```\n\n4. Modify the Model\n```\n\n```\n\n\n### 2. Seed Book Sections\n```\n$ npx sequelize seed:generate --name first-book-sections\n```\n\n\n### 2. List Book Sections\n\n### Resources\nAssociations in the model definition: https://sequelize.org/docs/v6/core-concepts/model-basics/#column-declaration-shorthand-syntax",
"sectionType": "section",
"sequence": 2,
"parentSection": {
"parentSectionPart": 3,
"parentSectionType": "chapter",
"parentSectionSequence": 1
}
}
]
}
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment