 I don't know if you're like me but, you know, each year you may make a resolution or two and a resolution that I tend to make every year is I want to get healthier, I want to lose weight and exercise and sometimes I even stick to it. A good way that I have found to keep on track is to not only make a goal, but to track the progress towards that goal. There's something about visualizing progress that helps me to stay motivated and to stay on track and to want to kind of continue to push those numbers. I think that's why devices like the Fitbit and, you know, other wearable technology has been so popular because it becomes, it's kind of like gamifying the system, right? Figuring out what it is that you can do to motivate you to continue to get better. So in this tutorial, I want to walk through creating a modern Node.js application to keep track of weight measurements. We'll use some technologies like Postgres and a brand new Postgres client for Node.js. We'll use ViewJS on the front end and we'll use, of course, Okta to secure the API and add account registration and login and all those kinds of things that are no trivial thing to do for any application, but Okta helps to make that so much easier. And before we begin, if you are following along, there are some prerequisites for doing this tutorial that those include having Node.js, of course. You're going to want to have Node.js 12 or higher. At least that's what I would recommend. You could probably get away with Node.js 10, but the more recent, the better. Node.js 14 is out. It's not yet part of what they call long term support, but 12 is like the stable version of Node that's useful for anybody in any situation. If you're on Linux or Mac and you don't already have NVM, that's Node version manager, NVM, man, that's a fantastic tool to get a hold of to use to install new versions of Node. You could have multiple versions of Node installed at the same time and be able to switch between those. So if you want to preview version 14 and play around with it, that's the kind of thing I do is I'll install using NVM, whatever the next release of Node is coming out, but I'll keep a stable version for building actual applications and things that I'm going to put into production or as tutorials or whatever. I'm going to switch my view over to my coding screen and you can see I've got, this is the tutorial that I'm planning to walk us through. It's build a wait tracker app with Node.js and Postgres. If you want to, you can go to developer.octa.com and click on the blog link at the top and you will find this article in the list of posts that are out, that's out there. If you want to follow along with me using this article, that'd be great too. Or you can, you know, I'm just going to walk through. I wrote this several, gosh, I'm not sure, I guess it was a few months ago. So I've slept since then and so I'm looking at this brand new as if I had never done this before. So this is all new to me too. I haven't looked at this tutorial in weeks or months and I don't even remember what all is in store for us as we go through this together. So again, it's going to be a nice learning opportunity for all of us or at least an opportunity for you to, you know, watch me bumble around. So there's an animated GIF that at the beginning of this tutorial that shows what we plan to build. We're going to add a login screen, account registration screen. After you log in, you'll be able to add measurements. Well, that tells you right there how long it's been since I've worked on this tutorial. That was back in January when I created this animation. So it uses a cool graph chart to visualize the progress that hopefully you're making. I can't honestly say that I've made a whole lot of progress this year. There's just been, you know, there's been a whole lot of life happen in the last six months. Wouldn't you, or last five months, wouldn't you say with the virus and everything else? So I can't say that I've really stuck to my resolution goals as well as I wish I could have. And then as I mentioned before, you're going to need no JS version 12 or higher. You're going to want a Postgres database. And if you don't already have access to Postgres or have it installed, that's okay. You can use Docker for that. And Docker is a fantastic tool if you don't already have it to be able to install services like Postgres or MySQL or even SQL server, Microsoft SQL server, which is my usual go-to relational database. So there's a SQL server for Linux that you can install in Docker. And of course, in this case, we're going to use Postgres. You're also going to need a free Okta developer account. This is free, free forever, free as in whatever metaphor you want to use. And just so that you know it's free as in, you can use it for up to 1,000 active monthly users, which is fantastic. If you're building an application and if it's just for some colleagues, maybe it's a small-scale application, it's just going to be in the dozens of people that are going to be using it. If you want to use it at work or use it among friends and family, absolutely use something like Okta to start out with. And then, you know, my goodness, if it does take off and you have thousands of active users and you're able to monetize that, then by all means continue to use Okta and ramp up to one of the paid tiers. But for this tutorial, all you need is the free version and won't have any issues at all with doing that. So let's, as the tutorial says, let's dive in and create this Node.js project. So you're going to want to open up your terminal or command prompt and change to wherever it is that you store your projects. And let me open up mine. So I'm in my projects folder. I'm going to change to my demos folder. And as the tutorial suggests, I'm going to make a Node wait tracker folder, change to that folder. And let's see. Where you need to, you know, the first thing we need to do with any Node.js project is to initialize that project. And we use npm init-y for that. Well, dash-y says take all the defaults for building an initialization file and just create a file with that. This creates a package.json file. If you don't specify the dash-y parameter, then it's going to prompt you for like name and version and, you know, what kind of license to use and so forth. There's ways of setting these defaults ahead of time so that each time you create a new project, it's consistent with the values that are put in there. Now, if you got an error message when you typed in this command, then that means that either you don't have Node.js installed or you don't have Node.js in the path for your system commands. So like on Windows, it would be somewhere in your path statement. Well, I mean, it's the same on every operating system, I guess. You know, there's an equivalent to that. So now might be a time if you got an error message to go and install version 12 and make sure that when you install it, if you're like if you're on Windows, there's a checkbox for, you know, to add. Node.js to your path. So either you don't have it installed or it's not in your path or maybe if you just now installed it and it's still not working, you may have to restart your terminal or, you know, hopefully you don't have to restart your computer. I sure hope we're beyond the days of having to do that all the time. So I've got the directory created here and just so that you can do things like MPM-V to see what version of MPM you have installed. I have 6.14.5 and Node-V gives you the version of Node that you have installed. I have 12.16.3, which I don't think is the absolute latest. As I mentioned before, if you use MVM, you can do one of the commands is MVM-LS remote version 12 and you can get a list of all the versions of Node that are version 12. Just cool. So in the future, I will reinstall install 12.17. Alright, so we're in our folder. Right now all we have is a package JSON and we want to install some more dependencies, things that we're going to need for our project. So this is not in the tutorial that's on the blog, but one of the first things I do, this is just a personal preference. One of the first things I do with every Node.js project is I want to install ESLint. And I want to save this as a developer dependency, so I use the dash dash save dash dev. So I'll install ESLint and ESLint config reverend geek, which is just my personal set of ESLint rules that I prefer. So ESLint is a linter or JavaScript that's going to catch common mistakes such as maybe you declared a variable but you never used it or you're using a variable that you didn't declare ahead of time. It adds some syntax checking and then just expression rules and all kinds of things that's going to help you to create consistently better code, better JavaScript. And we all know that we need all the help we can get with JavaScript, right? Let me make sure I have my chat open. If you do have any questions along the way about what I'm doing or about any of the specific commands or the modules that I'm choosing to install, please let me know. Type those in the chat or give me feedback, yell at me on Twitter, whatever it takes, right? Hopefully everyone can hear me okay. I think check the audio levels and as far as I can tell, the screen is set up correctly. I'm new to streaming and I'm learning as kind of by the seat of my pants, so to speak. All right. So now we need to install a bunch of other dependencies for this project. I'm using Happy, which is a web framework for Node.js that I personally really like. It's one of my favorite. Arguably Express is the most recognized framework for building web applications with Node.js, but there are some things about Happy that I really like. It's a little bit more opinionated than Express. I think it has a better module system, a more deterministic module system, so that you don't run into issues where modules are conflicting with each other or doing bad things with each other. Happy is going to throw an error if you try to register the same route more than once, whereas Express will allow you to re-register the same routes multiple times. That can be a really hard issue to debug and figure out why is this route not working like it's supposed to, and that's because some other module that you pulled in may be overriding that route that you've declared somewhere else. There's lots of other opinions. I just, you know, I really like it. And we're going to install Happy 19, Happy Bell. Let me switch over to my browser screen here, and I want to show you some of the modules that we're about to install. So Happy is the web application framework. Bell is a plug-in for adding third-party logins. Boom is a plug-in for HTTP errors. This is another reason why I like Happy. I love the names of the modules. I like to have fun. I, you know, I hope you like to have fun too, and so why not have some fun with the names? Cookie for cookie-based authentication, inert for serving static files. Joy, Happy, Happy, Joy, Joy for the plug-in for validating requests and responses. Vision is for HTML templates. We're going to use the .env for managing environment variables in a environment file. EJS is a template engine based on JavaScript. It's embedded JavaScript, I believe, is what EJS stands for. And then Postgres, a fairly new Postgres client that has come on the scene recently that is pretty exciting, I think, with some of the features it has. And then another, one of my favorite modules for any Node.js project, at least any web application project, is NodeMon. It automatically monitors your project folder for any file changes and automatically restarts your Node application with making sure that you don't use NodeMon in production. This is only a developer tool. There are other process management tools that are specifically built for production for, like, hosting and managing a Node.js process. So we need to install all these dependencies and install, you know, these relatively specific versions. So I'm going to copy and paste all of this stuff into the terminal window and just run those as they are. Just for compatibility purposes, I've versioned those. Normally when you install something, you don't say, you know, you don't specify a version number, like at happy slash happy at 19. The happy project and its other dependencies or its other modules are namespaced. So that's the reason for having that prefix of at happy slash and then the name of the module. And then the last at, say, at happy at 19, that is specifying happy version 19. That's just because when I wrote this tutorial, these are the versions, the major versions of these modules that were available at that time. There could be breaking changes for some of these that may not be compatible with all the code. So just to save ourselves some frustration, we're going to stick with those versions. So now we have all those things installed. If I look at my folder, we now see that we have a node modules folder and a package lock. Node modules is probably a bajillion files in there by now. As, as, as you may have seen the joke, you know, the, what's the heaviest thing in the universe? You know, it's not a black hole. It's the node module. Yeah, it's a struggle. All right. So now I believe we can start maybe setting up some more configuration and start with some code. So before we get too far in the, in the app, let's create a file name.env, which will be for our, our configuration like a port number, our host name, and later on some, some things like our client ID and client secret and things like that that we need for, for login and security. So one way you can do that, if you're on the Mac or Linux, you can use the touch command touch.env and that, that will create an empty file. I, I really like that, but you don't, you can use, you can go ahead and open this folder in your favorite editor and, and start adding files and folders that way too. I'm going to open this up in Visual Studio Code and see that we have a, our node modules folder and our empty .env folder. And I'm going to add port equals 8080 and host equals local host. Oh, one thing I didn't do, I went on this whole spiel about why you should use ESLint and installing those dependencies. As you can see in the developer dependencies, we have, have these things. We need to set up a configuration file for ESLint too. So to do that, I'm going to create a ESLint rc.js file and I can never remember what's supposed to go into this file. So here's a, here's a tip from the command line. You can, for any, any module that you have installed, you can type npm docs and I'm going to say ESLint config reverendink. And that's going to open up the documentation page for that module. Fantastic. I don't have to go searching for it or anything. It just, it just opens it up. So I know that I want this right here. This is the, the rule set for ESLint that is for Node.js and Common.js modules. I'm going to copy and paste this into my ESLint rc.js file. Boom. Now I have ESLinting. And one of the first things it tells me is you don't have use strict. Right. So here's another tip that's a good idea for Node.js applications. You may be used to in the browser or when you're using a tool like Babel for transpiling or if you're a typescript person and you're used to writing typescript and transpiling that to JavaScript. All those tools will automatically add use strict at the top of every file. And if you happen to be fortunate enough to use the latest, latest ES modules such as the import and export statement with browsers, those types of modules automatically have strict mode turned on by default. But in the world of Node.js, at least for the time being, if unless you're using Node 14 and able to use imports and exports, every JavaScript file is not running in strict mode by default. So any of the common JS modules. So that's one of the ESLint rules that I put in to remind me when I create a new JavaScript file, I need to add the use strict keyword at the top of every single file. What does that do for you? Well, it's just another safeguard to keep you from making some silly mistakes or from running into some older, unexpected JavaScript behavior. So that use strict mode kind of turns off some bad behavior that JavaScript has been known for in the past. So cool. Always always have strict mode turned on for Node.js. It is not on by default unless you are using import and export. All right. Now let's still following along in our tutorial. So in the tutorial I've got here talking about Visual Studio Code. If you don't already have a favorite code editor, I highly recommend Visual Studio Code. I love it. Use it every day. Can't believe just how awesome this thing has become over the years, the last few years. It is one of the best, if not the best JavaScript debuggers available, in my opinion. So we create our ENV file. Next we want to create a folder named source and then source creates some folders called assets, plugins, routes, and templates. All right. So from the command, I'm going to do this from the command line. You can do this from within Visual Studio Code, but I just like running commands. Make their source, see assets, plugins. So now if you look at your project, you should see this structure. This is how we're going to organize code. Now there's as many opinions about how you structure a Node.js project as there are JavaScript developers. Everybody has an opinion about how to do it. This is the approach that I like when I'm building a happy Node.js application is to have the source folder for the server side code and just have it broken up into these folders. Now I believe in showing real world, like not just a hello world. Hey, I could put everything in one giant JavaScript file and show you that it works. I want to show my goal in any time I create a tutorial is to show you what I think are like at least next level structure for building an application. So instead of having an assets folder, we could just have an assets.js file and throw everything into that one file. But I don't think that's doing you any favors if I show you an approach to doing it like that. I like to go ahead and set up some structure to my application because I know as the application grows, I'm going to need some organization. I'm going to need to have multiple modules in assets, multiple plugins, multiple routes. And I want to organize those routes into separate files so that they're logically grouped, right? So I don't think I'm doing anyone a service if I just show the quick and dirty hello world approach to building something. But that being said, let's create a hello world web app with happy. So in the source folder, let's create a new file index.js. Nothing magical, but index.js is one of the recognized files where if you tell node, hey, give me a module and just specify the source folder name. It's going to look for files like index.js and load that up automatically. So in the source folder, I'm just going to copy, hope you don't mind. I'm going to copy from the tutorial itself and paste that in here and we can walk through what this code is doing. That saves me from you having to hear me type and retype and, you know, just have a lot of flailing around. So we're going to again use the U-Strick at the top. Require in a couple of our modules, the .env, which is our environment, what reads in our .env file. Happy itself, we're going to load up our routes modules and we have a create server function that is an async function. Love being able to use async and await everywhere in node. That's so fantastic. So we're going to create our happy server using the happy server constructor and we're going to specify the port and the host. And we've also specified if for whatever reason the port and host are not defined in our environment variables that will default those to 8080 and local host. We're going to register the routes for the server and return that server from the function. And then we have an init function, which is also async and await. We're going to first read in our environment, initialize our environment, create our server, which is calling the create server function. And we're going to await server.start, so wait for happy to initialize all of its internals. And then we're going to run log to the console server running at whatever the URI is for that server. And then we have an unhandled rejection error handler so that if something does just completely crash and we don't have an error handler defined elsewhere in the code, it's going to log this out and kill the application. And then finally call that init function to kick off the server. All right, next we're going to create that routes module. So under source routes, let's create a new file and name this index.js. And we're going to define our very first route handler. Again, using use strict, we're creating a object here called home. And in this object, it has the properties method, path, and then a handler function. The handler function takes in a request object and a what's called the happy response toolkit. It's typically abbreviated as h. And, you know, as typical of a hello world application, we're just returning the phrase hello world is a string. And then we're exporting. This is where the magic happens when you're creating a module for Node.js is we're using the module.exports. And we're exporting an array of things. In this case, it's an array with only a single object in it with home. So that when we make this call in the index to require in our routes module, it automatically loads up the index.js file. We don't have to specify slash index.js. That is a default type of thing that happens for us with Node.js. Caviot defaults work with common JS, which is what we've had up until Node.js 14 Node.js 14. If you're using import and export syntax, you don't have this this nice shortcut. You have to specify the full path to any file, including the slash index.js and, you know, the JS extension. Bummer. I think I may stick with common JS. Just for that reason, I don't know, maybe not. All right, so we're pulling in routes. And we've got our routes to our routes define, which is just the one route. And now we want to launch our Node.js application. So what we want to do that is I'm going to we're going to go into package JSON and add a script that we can call anytime we want to to launch our application and use Nodemon to monitor for changes and restart it. So we're going to create an overwrite this test script with a dev script. And we're going to use Nodemon, set a watch parameter of our source folder. We're going to tell Nodemon to specifically monitor file changes to files that have the extension EJS and JS. And our start is source index.js. Okay. And now we can say npm run dev. And it's going to run that Nodemon script. Nodemon is printing to the console that it's starting up and gives you a hint there that anytime you want to manually restart the server, type in RS and enter, and it will restart the server. But it's going to be monitoring any change to any JavaScript or EJS file and restart that for us. So nice little shortcut on this particular terminal I have is I can hold down the command button and click on the URL. And it's going to open this up in my default browser. You can, you know, use your own, you can copy and paste that or just type it into your browser. But localhost 8080 and there's hello world. And just to demonstrate that Nodemon is doing what we expect, I'm going to switch over to here and go to routes. Instead of hello world, I'm going to change this to hello stream viewers, save that. And if we look at the command line, we see that restarting due to changes. And without having to restart NodeJS, I can come over here and refresh the browser and we have hello stream viewers. Success. We're on our way to building a sweet, super cool and awesome NodeJS application. All right. Now, now we get to the fun stuff. Let me take a sip of hot beer. We need Postgres. Part of this application is we're going to be using a database and Postgres is the example in this tutorial. If you already have Postgres installed and you're comfortable with using it and you've got your login credentials and all that stuff. And maybe you know how to create your own database and run scripts. Cool. Awesome. I'm going to walk through using Docker for setting up Postgres. Now, if you don't already have Docker installed, that's something you're going to need to do. So let's just take a look. Docker, maybe we'll go to Docker documentation. There's a getting started. There's a download and install. So it looks like Docker desktop for Mac is what comes up for me because I'm running on a Mac. Whatever shows up for you under get Docker, if you don't, like I said, if you don't already have installed, maybe you go in and do that now. It may take a while. Hopefully, if you were with me from the beginning and you didn't already have Docker installed, maybe you went and installed it while I was blabbing on about ESLint or something. But assuming that you have Docker installed, press Control C to stop Nodemon, I'm going to make sure that I have Docker installed. I'm going to type Docker PS. And hey, it doesn't give me an error. So sweet, I have Docker installed and I have Docker running. I don't have any containers right now. That's fine. That's what we're about to do next. I'm going to switch back to my code here for my preview, my own tutorial, so that I've got the right commands. And we're going to call Docker pull Postgres latest. And it's going to go through and pull down the latest image for the Postgres database. If you may already have the latest version of Postgres downloaded to your system, then it will just quickly go to the end. Like if I were to run this again, it should just go boink, you have it. Sweet. So now I have the image installed or downloaded. Now I need to create a container that's running an instance of this image so that I have a local Postgres database server running in Docker. And I'm going to copy and paste in a command to do that. And then I'll walk you through what this command is doing. So Docker run. So I'm running an image and I'm specifying dash D, which is it launches that container in what's called Daemon mode. Daemon processes. That's kind of a Linux or Unix term. So that tells it to run in the background. So I don't have to manually have Docker, this Docker command running. It's going to launch an instance of it and it's going to just live until either I shut it down or I restart my computer. It's just going to continue to run. So Docker run dash D. I'm going to name it, give it a specific name of measurements. So that just makes it easier for me to issue Docker commands so that I don't have to use some big long string of whatever Docker automatically assigns to it. Mapping some ports. So dash P. I'm mapping my local 5432 to the containers port 5432. That's going to allow us to transparently forward client requests to the Docker container. So from the code's perspective, it's no different than if Postgres was installed on my machine and running as a service on my machine, as opposed to inside a container. And then we're specifying an environment variable with the dash e parameter, and we're setting the Postgres admin password to some secure password. You can you can change this to whatever you like. This is all running locally. So not a huge deal, but certainly if you're going to install Postgres in production, you're going to want to set a more secure password for the admin account. And then finally, I'm specifying the Postgres image. So if you're running a running a an image as a container, the final parameter is going to be the name of the image that you want to launch. So boom, I got some big good here. Let's let's use Docker PS again. Hey, PS is, you know, shows the processes that are running in Docker. And I have an image called Postgres. I have it's the name is measurements and it was created 12 seconds ago. It finished booting up 10 seconds ago. If I run it again, yeah, 36, you know, so it took about a second or less than two seconds for this container to fire up. And now I have Postgres running. I think I mentioned this before Postgres will the database will continue to run indefinitely until either you stop it with the stop command. Like I can say Docker stop measurements, something like that. But if you restart your computer, just know that your containers are not automatically going to restart. So if you restart your computer and you come back and you want to play around on this project some more, then you ought then you need to use Docker start. And the name of the cool. Now let's add some configuration to our environment file. And I'm just going to copy and paste in some stuff here. So open up.env and somewhere below what we already have paste in our Postgres configuration. So we're setting PG host equal to local host username to Postgres database is also named Postgres by default. The password is the password we specified in the as an environment variable command line parameter and our command line argument. And then our report is 5432. If you change your password or you're using a different instance of Postgres that you have in your development environment, one way or another either it's hosted on your machine or it's hosted somewhere else on your network. And then of course you're going to want to change all of these values to match your instance of Postgres host, the username, the database, the password, report, if necessary, all that. Now to use a new database, we need a way to create tables to probably add some initial data and so forth. The way to do that is to create a script. And here we're going to use Node.js to execute a build script that will create that schema and data that we need to, you know, to initialize our database. And usually this is only done one time or each time that maybe your schema changes depending on how you want to create your initialization scripts. You may, you know, for a real world production database, you probably want to start creating incremental chain scripts that only run if the database is not in the same version as what you're building in development. So we're going to create a new folder in the root of our project called tools and under tools, I'm going to create a new file called initdb.js and I'm going to paste in a bunch of code and then walk through what this code does. So initdb, this is something that we're going to run outside of the web application. This is, we're only going to run this one time and that's to initialize our database. We can run it as many times as we want to. You know, anytime we want to kind of reset our database, we could do that with this script. So we're reading in our environment variables that are, that's in our environment config file. We're requiring in the Postgres client and we've got an init function that's going to read in those, those environment variables. And then we've got a try catch block to wrap around this in case there's any errors. Create an instance of the Postgres client which by default recognizes these environment, these very specific environment variables. So the client knows to look for PG host, PG username and so forth so that we don't have to specify that when we create the client. Then we're going to log to the console, dropping the table if it exists. We're going to drop, use that command drop table if exists measurements and await that. So this, this client that we're using the, the Postgres client is pretty cool. It's using template strings. There's a word for this that I'm that's escaping me right now. These are the, it's like an attribute type string where this is a command. I don't know, maybe someone in the, in the chat can, can help me out. If you happen to know what the name of this is called, but this is a it's executing this function with this template literal string. So we're going to await SQL and this is the command that we're passing to the client as a, as a string. We're going to create that new table that if not exist, it's a table name measurements. It has an ID that is not null. It's a primary key. It's automatically generated. It's an identity column. So all those are Postgres keywords that say this is an auto generated ID. There are a quick, there's equivalent syntax for doing this in my SQL and SQL server. You know, you can find those, those differences. We're going to create a user ID column that is a var car 50. It's not null. So we're not going to allow any nulls in there. A measure date with a date data type, a weight, which is a numeric field with basically this is up to a five comma one means it can be a number as high as 9999.9. And we're specifying a precision of one tenth of a decimal. That's what the, you know, five or we're storing five numbers and the precision is a single decimal point. Does that make sense? Same kind of syntax is equivalent in other data relational databases. And then we're going to, after we create our table, we're going to close our database connection. And if there are any errors, we're going to log those to the console. And then finally, we call that init function. In case you didn't know, async and await in JavaScript is really just syntax, syntactic sugar around promises. So we can call awaitable functions or async functions as if they were a promise. So we're doing init and then calling dot then as if it were at because it is a promise. And after all everything's done, we're logging to the console that it's finished. Or if there's an error, we'll log finished with errors. So now let's go back to the package JSON file. And let's add another script. We'll call this one init db. And the command that we want to run is node. Pass it the name of the file that we've created. And let's go to the command line, run npm, run init db, fingers crossed or whatever it is that you want to call it. Success, I think. So cool. We got an error message, but that error message was not fatal. It was just a warning saying that measurements does not exist. So it's skipping the drop statement. So it created the table and we're done. If we run it again, we're probably, we don't see that same warning message because measurements does exist. And so it drops the table if it exists, creates the table, and now we're done. We now have a measurements table in our database. Sweet. Now, next step, another big leap in our Node.js application is to add authentication to Node and to Happy. So when building any application like this wait tracker, you're probably going to want to keep your data safe and secure and private. And it would be nice to share this application with other people so that they can use it too. However, to build user registration, logins, authentication, password resets, email verification, all that kind of stuff. Man, that's no trivial task. And to get any of that wrong could expose your data to hackers, could expose your passwords if you're not properly hashing your passwords. There's just a lot of things that can go wrong and it's a scary space to be in if you're trying to add security to an application. So how about we keep all this concern and use a security expert to do that stuff for us? And that's what a company like Octa does. That's what they do best, right? Do one thing and do it extremely well and that's what Octa is known for. So Octa makes this pretty easy to do for any application in any platform. So to begin, you're going to want to go to developer.octa.com and click on the big sign up button or create a free account button. And if you don't already have an Octa account, if you do already have an Octa account, click on the login and go to your dashboard. After you go through the sign up process and you get your free account, you have to verify it through an email and that kind of thing. You don't have to have a credit card or any of that kind of stuff. After you sign up and you log in, log in myself as well, then you're going to come to your Octa developer dashboard and wrong screen. First thing you're going to do is after you have your account, you're going to want to create an application. So click on the applications on your dashboard, click on add application. And the type of application that we're creating for this tutorial is a web application. It's a server side application. So that falls under the category of platforms like .NET and Java and of course Node.js and lots of others. So click on web, click on next. And now we have our application settings. Most of these things we can leave as default. Let's call it my wake tracker. And instead of on the logout, instead of just the bare URL, let's specify slash logout. We'll create a route for this logout. Just in case somebody wants to log out of their account, we have an opportunity to clear that session cookie and so forth. Everything else can be left as default. Get down to the bottom and click done. Boom, you have your application. And there's a couple of things down here that we're going to need. And that, those things are your client ID and your client secret. So I'm going to copy and paste some code, some configuration from the tutorial over into the environment file. So move this up to the top. I'll explain what these things are. So not only do we want to have a host but also a host URL. This will come in handy for when we're building URLs and specifying redirects and so forth. We need a cookie and Crip password. This is something that's machine specific. It needs to be some kind of long string of gibberish, at least 32 characters long. This is something you definitely want to keep secure in your environment. I just have something silly here called super awesome password string that's at least 32 characters long exclamation. Super secure. And then specifying the node environment as development. And you can set this to production when you are deploying to production. That's something you absolutely want to do when you're deploying to production is to make sure that node environment is set to production. And then we also added these three environment variables, our octa or URL, the octa client ID and the octa client secret. That client ID and client secret are part of the application that we just created in our octa dashboard. So I'm going to click on this button to copy the client ID. I'm going to paste that in and do the same with the client secret. Normally you would never share this client secret with anyone. This is something that you want to keep secure because this is what your application is using to communicate with the octa application to ensure that you are who your application is, who it says it is. I'm not too worried about you seeing this in this tutorial because I'm planning to delete this after this application after the tutorial is over. So not that big a deal. The other third thing you need is your octa or URL. And where you find that is back on your dashboard. So if you go to your octa login and click on dashboard, your org URL is this right here. It's probably going to be different than what I have. It may not even be octa preview. It may be something else for you. But mine is this value and I'm going to copy and paste that into. Okay. Got the environment set up. And now another thing that you'll want to do as part of your octa application is, you know, wouldn't it be nice if people who you could just give someone the URL to your web app. And they can go in and register themselves and create a new account and use it, you know, without you having to create an account for them, right? That is something that has to be turned on in your octa application. It's not on by default. So that's called self service registration. And you'll find this under users and registration. So click on that. And right now mine is turned on, but by default it's not. So to wait to turn it on, you click the edit button, choose enabled, and then just scroll down to the bottom and click save. While we're on the screen, you know, it's interesting, maybe interesting to you to know that you can add other fields to your registration form. So if you want to include or require other values as part of your registration, you can do that. All right. So we've got that enabled. And now we need to update our code, our web application to start using octa for doing logins and registrations and all that kind of stuff. So switch over to our node app and under source, we're going to create a new plugin. So under the plugins folder, create a file named auth.js again, and for the sake of time and all the typing and stuff that would be required. I'm going to copy and paste in some code here and walk you through it. So in this auth.js plugin, we're requiring in Bell, which I mentioned before is for third-party logins. Cookie, which is for cookie-based sessions. That's typical of any web application regardless of platform is to use cookie sessions to store, you know, the current session information. And then we're specifying is this secure? In other words, are we based on our node environment variable? It is, if it's equal to production, then this is a, we're going to assume that our site is secure being secured with SSL, HTTPS. But if we're running locally on local host, then it's, then we're not, we're just using playing HTTP. To define a happy module, you export a object, and that object has some specific parameters. So very similar to when we created our routes, a new plugin has a name property. It has a version property, and it has a register function. Every, every mod plugin module for happy needs to follow this kind of pattern. So our name, the name of our module is auth version 1.0. And we have our register function that takes in the happy, the current instance of the happy server. So using the happy server, we're going to register our other dependencies, which are the cookie and bell. So we can pass that register as a, an array of those modules or plugins. And then we're going to configure our authentication strategy for happy. So this is a session, a cookie session strategy, the cookie, we're naming the cookie octa OAuth. We have to specify a path, set that to the root. This is, this is really important because if the path is, is kind of dependent on, if you don't specify the path, then I think it's dependent on the route that you're currently on. And yeah, it can have some unexpected errors where it looks like you've logged in, but somehow you're not logged in or somehow it's not recognized that you're logged in. That can be a really frustrating thing to happen. And it's happened to me enough times that it's like, I've got to remember to specify the path. So we specify the password that we're going to use to encrypt the session information. And that's the super cool and awesome cookie password that we specified in the environment file. You know, the super long and awesome 32 character password. And then whether or not this is a secure, this should absolutely be true if you're running this application in production. And then finally, where to redirect to if the person is not logged in or the session is being initialized. And that is our authorization code callback route, which we will define in a little bit. And then finally, we need to specify our authentication strategy with Bell. And that is, you know, for any other part of our application, it's going to check the session cooking. And if the session is valid, then it knows that we're logged in. But for the login itself, then we want to use Octa for that. So we're registering an authentication strategy with using Bell. The name of the strategy is Octa. The provider is Octa. So Bell already understands this. Bell has a number of third party strategies that it is equipped with that you can choose from. And Octa is one of them. And then we're going to use a config, specify that the URI for our org URL, the same password that we're using for our cookie sessions, whether or not it's secure, the location of our site, which is the host URL, the client ID and the client secret. So the host URL has to match when you're redirecting to Octa to log in and it's going to redirect back. All those things have to match up or it's going to throw an error somewhere along the way. Either the application is going to throw an error or the Octa login process is going to give you an error message. We're setting the default authentication strategy to the cookie session. That means for any route that requires authentication, it's going to default to session. And then we're going to explicitly specify on the login route to use the Octa Bell strategy. And then here's a really nice thing we can do with HAPI. This is like an added bonus to this tutorial. We're going to add an extension to the server that using the on pre-response event, we're going to check, is this response a view, which is like we're going, it's not an API route. It's a view where we're going to render HTML and send it down to the browser. We're going to check, is the person that's requesting this view, are they authenticated? If they are, then we're going to set some values or we're going to create an auth object that has these properties. Authenticated true, anonymous is false, the email address, the first name, last name. And then we're going to set on the context of this request to this auth object. There's a lot here to digest, but hopefully, if I can explain this, is that on every request, we're going to add to that request an auth object that is going to expose whether or not the person is authenticated or anonymous and some basic profile information like email, first name and last name. And what that allows us to do is that anywhere in our view templates, we can access that data when we're processing our view templates. And we can throw, creating an extension like this, we can throw whatever data we want to have, we want to surface on every request. Okay, so now let's add a new file to our plugins folder. Let's call it index.js. And in here, we're going to specify or put in this code. And we're going to pull in the inert and vision plugins from the happy framework, the EJS JavaScript templates, our authentication, our auth plugin that we just created. And then in this module, we're exporting an object that has a register function that takes in a server. We're going to register those plugins, the inert, vision and auth. And then we're going to configure the happy server to use EJS as the view template engine. So engines, we're specifying EJS, we're saying relative to the current directory of the application. The path is templates. So we're going to create, it's going to be in this templates folder. And then the layout, we're going to specify layout true. We're going to create a layout template that creates like the HTML head and the body tags and all that kind of stuff. So that we have just one place of creating the default layout for our web application. And then each view can just worry about specifying what gets injected in the body of that page. All right, let's add some HTML templates. So under the templates folder, let's create a new file called layout.ejs source code here. This template, we've got our head tag with some meta tags. Title of the HTML of this page is going to be equal to title. We're injecting that into this template based on the view that's being rendered. So from a model view controller idea if you're familiar with that metaphor, a view is combined with data and rendered together to generate HTML. So when we go to render this layout, this layout expects that one of the pieces of data that is passed contains a title. Hey, thanks for joining Rackbird. Have a good rest of your day and hope you do check back with the finished video. So we have some links to some style sheets. In this example, I'm using tutorial. We're using Bulma as a CSS framework. We've also got our own custom styles in a site.css file that we'll create later on. We're pulling in view version 2.6.11. We're also pulling in font awesome to give us like some icons and things like that. And then we've got our own site.js file that we'll create. EJS has, you know, some of the syntax should look a little bit familiar. If you've used templates and other applications, the concepts are very similar from one to another. Just the syntax maybe may look different. So angle percents are what is used to do things like inject values or inject includes or run commands or functions. So we're including in a navigation template, which we'll create, and then we're injecting the content. And that'll be the actual view that we are sending down. Now let's create that navigation template. So let's create a new file under source templates navigation.ejs paste in some more code. This is a nav bar and a lot of this I lifted from the Bulma documentation. The name of the application, the logo for the application is just wait tracker. And then we've got a a burger, a menu burger so that if if the width of the window is smaller than a certain amount, it's going to assume that maybe it's running on mobile. This is kind of like a responsive design using Bulma. And then we've got some navigation stuff here. We've got a home that just takes us to home, a progress button that takes us to the slash list route, an ad measurement button or link that takes us to slash add. And then we have like on the right hand side of the navigation bar, we have some profile information that if the current viewer is authenticated, then we want to give them a message welcome auth first name. If they're not authenticated, then we want to show the login buttons. Or no, if now we show some login or logout buttons depending on if the current person is authenticated. So if they're authenticated, if they're not authenticated, show the login button or login link. If they are authenticated, show the logout link. And now let's add a template for our home page. So under templates, let's create a file index.ejs. And we've got a title, Node.js wait tracker. And again, we're using some of that contextual information that's exposed through that server extension we added on the authentication plug-in, which checks to see if it's anonymous. And it's doing the same kind of thing like the navigation does. If it's anonymous, show a login button. If it's authenticated, show a weight measurement and other options to view progress. And then while we're at it, let's create a template for our 404 page. So if somebody tries to go to a link that doesn't exist, let's just throw up a sad response. Oh no, that page was not found. You can be as creative as you want with that. Now, come some fun parts. We're going to create some routes for our application like the login and logout routes and callback routes and lists and all that great stuff. And there's ways that we can use our authentication to make sure that whoever's accessing that route is authenticated. If not, we want to redirect them to login. So under routes, let's close some of these other pages. So under our routes folder, create a new file. And let's, you know, for lack of better words, let's just call it the auth, our set of auth routes. And I'm going to copy in some more code. We're pulling in the boom module, which is an error handling module for plug-in for happy. We have a login route, which is a just a get method with a path of slash login. And we're checking there's not not really anything we have to do here. It's just by default, our session strategy or authentication strategy is our session cookie. And if it's not authenticated, then it's going to redirect. So login is going to redirect. And just for as a backup, we don't have to have this in here. But if it's not authenticated, then we return an error message. We have our OAuth callback route, which is, you know, specified in the octa configuration by default is slash authorization dash code slash callback. So we're setting up a route to handle that information. And when that comes back, we're going to set our cookie auth with the request.auth.credentials. That's the payload of that callback that is coming back from the authentication. And then when we're done setting the cookie, then we just redirect the user to the home page. And we're specifying in the options that the auth method that we're using for this route is the octa strategy that we defined. We have a logout route that is also a get slash logout. And if they are authenticated, then call request.cookieauth.clear. We're going to clear that session cookie and then redirect them to the home page. And that's it. We export those three routes as an array. Next, let's create our, or let's open up the index.js file of our routes and let's register. Let's replace the hello world thing that we have here right now with the routes that we need for this application. So pull in path, which is a built-in Node.js module that gives us some utilities that we'll use. Pull in the auth routes that we just defined. So relative to this file, pull in auth.js. Let's define a home route that is the root of our site. Auth mode is try. So there are, let me see if I can remember. There's, the try mode is going to check to see if the user is authenticated, but it doesn't require authentication. It's not going to trigger sending that user to the login. It's just going to say, is there a session? And if so, go ahead and kind of initialize that authentication information. That's necessary because we want, we want to know on any given route that whether or not the user is logged in or not. So try is going to attempt but not force authentication. And then the handler is going to return h.view. So the response toolkit, this built-in to happy has this view function and passing into that function the name of the view that we want to render, which is index. And the second argument is the data that we want to pass to that view. The only data that we're passing for the home page is the title of that page. We need to define some route, a route for our static assets. And these are things like our CSS file, our JavaScript file, any images or fonts or anything else that we want to package with our web application. And we just want to serve those as files. We're not, we're not executing those files. We're just serving them. So we're using that path object to join together the current directory of this module along specifying that everything that's in the assets folder. So whenever you get a route, a request for something that starts with slash assets slash and then some like file name like site.jpeg, site.css, site.js, it's going to pass along. It's going to attempt to handle this request using files that are in the assets folder. And authentication is not required to access any of these files. Any of these files are available to the public. Specify our 404 page. So anything that is not already caught or registered explicitly with the routes, then we want to return our 404 template with a title of not found and also send down a HTTP status of 404. And again, authentication is not required to view the 404 page. And then finally, we're going to export an array of these modules or array of these routes along with the auth routes that we pulled in from the auth module. So those are all our routes that we have for the application, I believe. Now let's add our static assets. So an assets folder. Let's create a file called site.css. That's it. We're going to specify our navbar logo has a font weight of bold and a font size of 1.2 em. And now let's create a site.js folder, our file, and in here add some code. And this is basically straight out of the Bulma documentation. This is not necessary other than if we want to have that responsive design where if the window gets too small, like somebody's looking at this on a phone. Or a tablet. Then it's going to the navigation bar at the top of the page is going to change to that hamburger icon. And this code sets up handling the showing and hiding of the menu. So it's going to select the navbar burgers. And again, I lifted this code straight off the Bulma documentation. So if it doesn't work, then I don't know, maybe blame them. But it's going to add a listener for a click event. And in that click event, it's going to toggle the showing and hiding of that menu. Sweet as. All right. So now let's, I think we may be ready to test what we've done so far. Let's switch over to our command line and run npm run dev. Oh, route missing authentication strategy and no default defined. Well, it should been defined. Maybe I skipped a step. This is where the live streaming code gets interesting, right? Debugging what should have happened. So let's server routes. We're pulling in routes. We are registering those routes. Plugins. We, we have our auth plugin. Am I registering plugins? No, I am not. So I must have skipped a step somewhere in there. We need to register our plugins as part of the server registration. So we're going to require in that plugins folder, which by default is going to read in the index.js. That index.js pulls in all the modules that all the plugins that we have defined, including the framework plugins. So we need to register plugins. I think maybe we specify this a different way. Okay. So we did this a little bit different. So instead of calling server dot register, we actually need to call this register function from that we're exporting. So instead of this, we're going to say plugins dot register, passing in the server and let's await that. That should give us everything that we're looking for. Okay. So now the server is running without any startup errors. Fingers crossed. Boom. We got it. We got an error message. Okay. So implementation error on our layout. Part of the include could not find the include file includes navigation. So maybe I did I. Oh. So I think the way I have it set up in the registration or the way I have it set up in the layout is that it looks for the path includes. And navigation is in the includes folder. Let's move navigation into the includes folder. Node mon saw that we made a change automatically restarted. So now let's go back to the homepage restart refresh. Yay. We have our wait tracker and it has a log in. Welcome to the Node.js wait tracker sample project click here to log in. Okay. Now I could click on this log in. And either I would be prompted to log in to my octa account or I might get automatically redirected back to the application with a with a session. And that's because, you know, octa also keeps track of if I'm logged in or not. So in order to make sure that this is working as we would expect, let's launch a private window. Remember the command to do so. And we're going to go to local host 8080. Click the log in. Boom. We got our log in form. We're going to log in with your octa developer credentials. And you'll notice that, you know, our registration self registration is enabled. So don't have an account click sign up and that that will go to the registration form. So we'll click sign in. We got redirected back to the home page. And we now have. See our message. Welcome, David. There's a log out instead of a log in. We got something wonky going on with the menu is not working quite right. But this is this is looking good. So successful. So Lee MW 1977 says don't have much time to spend with you folks today. We want to pop in to say hi and hope you hope you're well. Hope you're well. Lee. Thanks for dropping in. Hope you're hope everyone watching this is is doing well. All right, we're we're on the home stretch. We have our our log in is working. Our views are working. Maybe our CSS isn't working quite right with the navigation menu. But that's something I'm sure we can we can figure out if we really dove into it. If I resize this window, of course, it stretches out beyond what you can see. But you can see that the, you know, beyond a certain threshold, the navigation bar looks looks okay. Looks like it's supposed to. I'm going to close this and also close this one. And let's see what what do we do next? I know we got at our database stuff and measurements. So let's create the next start part is to create a secure API. That we can that talks to our postgres SQL database. And we can do some interesting things around adding measurements, retrieving measurements showing that that pretty graph and and so forth. Authentication is working. We can now focus on our API. Our first first thing we want to do is create a plug in for our database. Our SQL client. So in the plugins folder, create a new file SQL.js. And let's look at some code for that. So we're in this plug in. We're pulling in the postgres client. We're exporting a our plugin object, which has the name and version properties like we've seen before, and the register function like we've seen before. In the register function, we're going to create an instance of our SQL client. And then we're going to use this special function called server dot decorate to add our SQL client to the request toolkit. What this is going to do is now every time in a such as a route that we define anywhere that we see a route. We can access the SQL client from within that route by just typing in h dot SQL. Pretty cool, huh? We can create plugins this way where we can decorate our request toolkit with anything and everything that we need to use throughout our Node.js application. So next let's create a, well, we need to update our plugins index to include that new SQL plugin. So underneath off, let's require in our SQL plugin. And then as part of the register, let's include the SQL plugin as a register. Good to go. And now under source routes, let's create a folder to include all of our API routes. Again, you know, creating some structure to our Node.js application. So under API, let's create a file named you guessed it index.js. And in here, paste in a whole bunch of code that will walk through together. Now, I'm putting this, I'm kind of going back and violating something that I said earlier about structuring a Node.js project. That is, I'm just throwing all of this into one big index file. We could separate these out into separate, you know, file like a file per route, like under API have a file named add measurement for current user, get measurement for current user by user ID, all these different routes. We could, we could divide those into separate files and pull those in. That's great. I mean, if you want to do that, that either it's just a personal preference. And as a project grows, maybe you go and refactor your project structure to make to whatever makes more sense. It's going to make it easier to understand and easier to maintain. So in this API routes module, we've got a whole bunch of routes defined. The first one that's listed here is the add measurement for current user, and it is a post method. So we're posting data to this API endpoint. The path is slash api slash measurements. And we're going to say, if we're going to repeat this pattern with all the routes in here, but if the request is not authenticated, then we're going to return a boom dot unauthorized error message. Now, the reason we're doing this, instead of using the built in auth, you know, like we defined when we registered our auth, we set the default session, the default authentication strategy to session. When we're calling an API endpoint, say from, and later on, we're going to be using Vue.js and probably Axios, if I remember correctly, to make API calls. We could use the fetch, built in fetch object as well. We don't want to, when we make a request to an API endpoint for that to somehow be redirected. It's not going to be visible to the person who is using the application. It's kind of an internal thing. And an API generally doesn't return redirects anyway. It returns specific error codes that the application or the JavaScript can then respond to and handle accordingly. So one way we can do this is just by saying every one of these endpoints has an auth of try. The auth mode is try, so we're going to attempt to check to see if the user is authenticated, but we're not going to enforce authentication, meaning we're not going to redirect them if they're not authenticated. But if they're not authenticated, then we send that unauthorized error message. So that short circuits the route. Anytime a person is not authenticated and they try to access one of these API endpoints, they're going to get that unauthorized error message. So we know that the person is authenticated. We're going to get their user ID from their authentication credentials. That's part of their authentication profile that's provided by Okta. There's basic profile information. And then we're going to extract the measure date and weight from the payload that's being sent to this API endpoint. And we're going to call insert into our measurements table with user ID, measure date, weight with these values, user ID, measure date, weight. And when we're done, we're going to return these values from that insert statement. Okay, now you're probably looking at this and thinking, wait a second. If you know anything about doing stuff with databases, you know that using templated strings and just putting stuff into strings is a bad idea. Trademark bad idea because of SQL injection and tax. Someone could specify as a parameter or a field, you've probably seen the classic little Bobby Tables cartoon, right? They could create an injection attack that actually short circuits the SQL statement and causes bad things to happen. Okay, but this SQL client that this Postgres client that we're using has SQL injection or rather I should say has parameterized SQL queries built in. It's going to parse this templated string that we're passing and it automatically creates for us a parameterized query and it's going to pass these values as named parameters. I'll let to say that's going to handle or prevent SQL injection attacks even though it looks like this code may be vulnerable to SQL injection attacks. Less boilerplate, less code that we have to generate, this client is pretty cool in my opinion. Okay, so that is the API measurements post. There's also as part of the options we're using a validate property that uses the joy plug in for validation. So the payload is expected to be an object that has a measure date property and a weight property. The measure date is a date data type and the weight is a number data type. If this API endpoint is called with something else like it doesn't have a measure date and a weight, then it's going to return an error message. It's not going to run any of this code because the data that it's receiving is invalid. So joy is a really awesome and super flexible input validation tool that we can it's very expressive. You can do a lot with it and this is just, you know, one little tiny scratch on the surface of what you can do with the happy joy plug in. All right, so our other endpoints we have, we have an API measurements get endpoint. So it's the same route or same path, but it's a get method instead of a post method. And it's going to return all of the measurements for the current user. So it's going to first check is the user authorized. It's going to get the user ID of the current user. It's going to select all the measurements where user ID is equal to user ID and order those by measure date. So ascending quarter. Super cool and awesome. It's going, you know, this again, this postgres client, we can say measurements is equal to a weight dot h dot sequel and measurements is, you know, our, our array of rows from the database. Super cool. We have a delete method. That is the path is API measurements slash ID. ID is that parameter query, not query parameter, but it's, it's part of the path. And so it's going to pull the ID from request dot prams instead of request dot payload payload is is like the data that's posted on a post or put command. And prams is the how you get to query, query string print or path type objects. And if we're going to, we're going to run that delete. And if delete succeeded, we'll return a 204, which is like a no data. You know, there's no data that required to return from that API employee. Or if there was no rows affected, then we return the not found. And again, we're validating that the ID is a number and it's integer. Get measurement by excuse me by current ID, current user by ID. So we're using a parameterized path, a parameterized query, where we're selecting a specific measure by user ID and ID. All these queries are taking into account the authenticated user's user ID. So in essence, what we've, what you're creating here is a multi tenant application. So that anyone who uses this application, they're only going to see their data. They're not going to accidentally see anybody else's data. There's a query query that's done against this database, either inserts, updates, deletes, you know, selects all the basic CRUD operations. Take into consideration the user ID that is part of the login for that current user. We have an update, which is a put method that takes in an ID. It also has a payload of measure date and weight. So for some reason we wanted to update a particular measurement to change the weight or the date for that measurement. We can do that through the API. And then finally, so those are all the CRUD operations for the API. And finally, we export an array of all those routes. Well, let's take another moment for a warm beverage. We're getting there. Let's create a view for adding our measurements and tracking our progress. So under source templates, let's create a new file called add.ejs. This is going to be a whole bunch of stuff. This tutorial is really focused on the Node.js aspects, the authentication, the server side things, using Postgres. So it's a little bit out of scope of the tutorial to go into really in depth into what is being done here with View.js. But I'll walk through some of these things and you can see, hopefully, understand how View is being used on the front end for doing these operations. So our app is what View uses to register a View application. So we have an ID. We have this div tag that holds everything that's on this page. Our title is an add measurement. We have a form. And we're using app submit.prevent is a View thing to prevent that form from being redirected. Instead, we want to call this function that we're defining in the JavaScript code to make an API call to add a weight measurement. And so we have a date field that is an input of type date. And we have some placeholder text. And we're binding this date field to our model of model property of measure date. And we're trimming that value at the same time. Same goes for weight. We're specifying that our input is a number. And we can throw some interesting properties on this input to say it can incrementally be values of .1. Minimum is 0. Max is 2000. And we're setting our V model or binding this field to the current weight value. And then we have a submit button and a cancel button. And then for purposes of showing messages and error messages, we have a couple of placeholders for that information. So in our script, JavaScript for this front end, we're creating a new view application, passing in that app that we defined up here, the ID of our div tag. And in the data for the view application we're returning, basically this is the default. So when the form comes up, the date is going to be automatically populated with today's date. The weight is empty, or in this case undefined, so that it's got an empty measurement disabled as false. We're going to use that disabled to toggle the form disabled and enable when we're actually making calls to the API. And then we have our add weight method, which is an async function. We're going to set our data disabled to true, which is going to trigger that form being disabled while this add weight is being called. And we're using the, okay, I thought we might be using Axios, but instead we're using the built-in fetch to make a post to API measurements. And we're sending application JSON data, and we're going to stringify our measure date and weight from our data. And if the response is 200, then we're going to reset the form and show the message that our weight measurement has been added. And after a couple of seconds, we're going to clear that confirmation message. But if there's an error, then we want to get the response JSON, show that JSON error message in the error placeholder, and then finally re-enable the form. And then we also have a format date function that's going to give us a nicely formatted date instead of the JavaScript default. Good deal. And now let's create a new template called list.jejs that's going to list all of our measurements and give us that pretty chart. Copy and paste in a whole bunch of code here and walk through some of this. So I want to give us more real estate to look at this code. I should have done that earlier. That is command B or control B, I guess if you're on Windows, will show and hide the project folders and files. So in this, in here, we're pulling in the chart view plug-in and the chart dependencies for showing our pretty chart. Again, we have a div tag with ID equals app for ViewJS. Title, we have a placeholder here for any kind of error messages that might come up, and then we have a conditional div tag. If we're currently fetching, then we'll show this HTML element getting your measurements. Or if we have data, then we'll show everything else. So we have our line chart and the data for our line chart is defined as this chart data function. We have a table and this table has three columns, date, wait, and then a column for some actions, some buttons. We're going to iterate over the measurements that are returned back from our API. And for every measurement, we're going to create a new row that includes the measure date, the wait, and a delete button. If we, for whatever reason, we want to delete that measurement, we can click that button and it's going to call remove passing in that current object. If there's no data to display, then it's going to show this message, no measurements to display. Now we have our script. We can, we register the chart plugin that we're using and then we create our view app. Again, specifying that the element that we're basing our view application on is the app element. We have some computed properties that we're using throughout that no data function is that computer property is equal to whether or not there are any measurements. The has data is also are there any measurements in our measurement set. And then we have our chart data function, which basically takes the data that we're getting back from the API and just restructures it. It reformats it to be in the data structure that the chart expects. And we have a chart minimum computed property that is based on what is the largest weight measurement and we're adding some padding around that. In order to get, this is to set the scale of the current chart so that the chart, you know, looks pretty relative to the minimum maximum values that are represented by all the data that's in that chart. We have a in the mount, let's see. So our data function, this is like the initial data set. We're returning an empty array of measurements fetching is equal to false error. There's no error message. So this is just, you know, populating the default values for the form in in the mounted event or the mounted function of the view application. So this is like, this is fired one time when the view application finishes initializing. So in the mounted event, we're going to fetch all of our measurements from the API. And that method is defined as a method. It's an async function. It's going to, you know, first initialize all of our data again. It's going to fetch that API measurements using the get parameter. Up here it's setting fetching equal to true that way it displays the error, the message that currently fetching data. And then we're after we're done, we set fetching to false so that it hides that message again. And then if the response is 200, which is okay, then we're going to get all the JSON data. And we're just going to map that JSON data and do some manipulation on it to clean up the dates and return the ID and the measure data is the current viewer's locale for that date string and the current weight. And then we have a remove function that is first going to pop up a confirmation. Are you sure you want to delete this measurement? And if so, then call that fetch API to measurements and so forth. That is all. Let's see. The last step is we need to update our routes for these new views. So let's create a let's go to the source routes folder. Let's close these other files. And in the routes folder, let's create a new file named measurements. We have a add route and a list route. And really we're just rendering our templates, specifying the title of those pages. Nothing fantastic here. Just rendering those views. In the source routes index, we need to add our measurements. So oh, you know what? Something I missed earlier. We didn't add our API. We need to do that here too as well. API measurements. All we need to do is to add these to the array that's API. Now for the moment of truth. Our server's been running this whole time and it's detected every change I've made. Every time I've made them. So it's restarted. So it's ready to go. Our server's ready. Good news is there's not any errors. That's reassuring. Let's go to the browser and let's open up the incognito browser. Go to 8080. Everything's good so far. Let's log in. Good. Still don't see, you know, the menus are still a little bit messed up in the responsive mode. But we can still use these links down here. We can go to add or view. Let's click on view first and we get what we expect. It says no measurements to display. So that's also available under this progress. Let's click on add measurement and it defaults to today's date. And let's say 220.5. I wish I weighed 220.5. I'm being optimistic. So click submit. We see our error miss or not error miss. We saw a message saying that a measurement had been added and then it went away after a couple of seconds. Let's go ahead and add another measurement. Let's add one for yesterday and let's say it's 221.5. We lost a pound in a day. That would be cool. And let's back up even further to May, let's say, 223. Now if we go to our progress, we see a nice trending downward line that we are on track for losing weight. And we have our individual measurements listed here in the chart. If we want to, we can play. I don't like that old one. Let's just delete it. Okay. Boom. Now we only have two measurements. Wow. That's a lot. A lot of stuff in there. But we made it all the way to the end. Some of you are gluttonous for punishment. Because it is then over two hours. Hi, Toe Frog. Love your handle. You got a question for me? If you do have a question, please post it in the chat. Anyone that's still on the line, I'll answer some questions if we have any for a few minutes. Trying to think if there's anything else I should cover as part of this session. One thing I'll give you while you're here. If you're interested in OAuth and OpenID Connect or security, creating secure applications. There's a lot of confusing jargon and terminology and conflicting information out there as to what OAuth and OpenID see. OpenID Connect are or OIDC. And I've created a video that I call the illustrated guide to open OAuth and OpenID Connect. I'm going to paste that link in the URL. It's both a blog post and a YouTube video. The YouTube video is included at the top of that blog post. But if you're interested in seeing that, it's about a, I want to say it's 16 minutes long. Illustrated introduction to OAuth and OpenID Connect. I hope that will serve a good foundation. It's not specific to OID. The OAuth and OpenID Connect are standard protocols on for the Internet for how to handle delegating access to applications using a trusted login provider like Gmail or Google or Facebook or LinkedIn or your own custom OAuth provider. So I hope that guide would be useful to you. A lot of people seem to have found it useful. That's it for this stream. I hope you have found this entertaining, if not maybe a little bit interesting and maybe you learned something along the way. Maybe you learned that HappyJS is worth checking out or maybe that the new Postgres client is pretty cool and you might want to use that in your next Node.js project, whether you use HappyJS or not. All right, folks. Well, until next time, and as always with anything that I do, I want to encourage you that you don't need permission to be awesome. I hope you take the things that you've learned here and you get out there and you do some amazing things, things that I would have never even dreamed or thought of. I love hearing stories of how things that I've shared have planted seeds or planted ideas to go and do amazing things. So if you have any of those stories in the future that you want to share with me, I'd love to hear them. I hope you have or having a good day. I hope you have a better week. I hope you are doing well and I'll see you next time on The Streams.