What do you want to learn?
Skip to main content
Building Web Applications with Node.js and Express 4.0 (UPDATE)
by Jonathan Mills
Start CourseBookmarkAdd to Channel
Table of contents
Introduction and Installation
Welcome to Building Applications with Node.js and Express and this is a course that's going to walk you through getting a whole website up and running with Node.js and Express and Node is kind of getting to the point where it's taken over the world just a little bit, where you've got Visual Studio Code that was built on Node and you've got Electron applications sprouting up all over the place and Twitter and Walmart use Node for their backend, it's just kind of everywhere and so this is a course that's going to get you started moving in the direction you want to move in. So what are you going to learn in this course as a whole? And, like I said, we're going to build a web application. Just from the ground up, we're not going to use a CLI or anything, we're actually just going to build the whole thing all the way through. We're going to use Node and NPM. NPM is the package manager for Node and we're going to use somewhat close to the latest version of Node. Node's going to change quickly, we'll talk about that in a minute, but Node and NPM are the core foundation of what this course is going to be. We're going to talk about templating engines. There's Pug, which used to be Jade, there's EJS, there's a few others. We're going to talk about those too and I'll show you what they are. We'll talk about NMP scripts, that's what we're going to use to build out all of our processes around our application. In a previous version of this course, we've used Gulp, in this version of the course we're going to simplify a little bit and just use NPM scripts because the vast majority of what you need to do is just going to work just with that. We'll talk about routing and setting up multiple pages and just building out the framework for a full web application and databases, we're going to do databases a little different, we're going to look at two different extremes. We're going to look at Mongo on one side that is a no SQL database, a document store kind of database and then we'll look at the other extreme with SQL Server. I want to show you SQL Server specifically because there's many enterprise shops, dot net type shops, that use SQL Server and I want to demonstrate to you that that's fine, you can use SQL Server and Node at the same time and then we're going to consume APIs. In our application we're building out a book list and we'll have Good Reads as a backend API that we can go to to pull images and descriptions for our books. So let's get started. We're going to talk just a little bit in this module about Node and how it works and all those types of things and then we'll get started building out our web application.
What Is Node?
So the first thing we need to do to get started is to install Node, that may seem fairly obvious and some of you may have already done that but I at least want to talk about it just a little bit. So head out to Nodejs.org and you'll see on the front page, maybe somewhere on the front page, a download for your specific operating system and here I've got 8.9.4 for LTS and 9.5.0 for Current. The only thing I can guarantee you is that's not the numbers you're going to see because Node versions change rapidly and we'll talk about that here in just a little bit. One thing I do want to say is make sure you're at least after Node 7.5 ish because we're going to use Async Await to do some things and that came about a little bit later so if you're on an older version of Node, stick around, I'll tell you how to deal with that here in just a couple of clips. So you can either click right here or you can go to the downloads tab and the downloads tab, you can download for Windows, Mac OS, or you can get source code. You can download Sun or for Docker images there's all kinds of options but what I'm going to do is I'm just going to click on the 64 bit installer, the Mac OS installer, I'm going to download that. I would suggest you download whichever one is appropriate for you, run through the installation process. I'm not going to walk you through it because you could run this literally on any platform and so I don't want to do one and then have three quarters of the audience not get it so at this point, you should just be able to walk through installation and go from there. Once that is done, you should be able to pull up a command line and type node minus v. Hit enter and you'll see right here 8.9.4, that's the version I just installed. As part of this, it'll also install NPM and NPM is the package manager for Node and its version will be very different. So type npm minus v and you'll get 5.6.0 or some completely different number. Node does a very good job of being backwards compatible so I wouldn't worry at all if you have a fundamentally different version. If it's up to Node 12 or whatever, it doesn't matter as long as it's post mid sevens and I'll show you a way to figure that out here in a little bit and I'll show you a website that helps you figure out what features are available here in just a little bit. So once you're here and to this point, you should be good now let's go talk about this Node version thing.
Dealing with Node Versions
So we can't really talk about Node without talking about Node versions because versions change pretty rapidly in the Node world and that's a good thing, we like change, we like the additions that they're making but we need to deal with this and if you look at this screen, you'll see that Node changes very rapidly. Node eight was current right up until about November of 2017, Node nine came along and will be current until about April but then Node 10's going to come along in May or so and be current until October and so you'll go through some changes fairly rapidly. It used to be current for three months and that was it, it's gotten a little bit longer than that but also notice how the actives work. Node eight is active and Node 10 is active, so that's the long term support version. The odd numbers, Node seven and Node nine, don't ever hit LTS or active and that's just the cadence that they've struck with their versioning. So with that being said, how do we deal with versions because you might be working on a project on Node six, this course is late Node eight, you may have Node 10 come along and there might be a breaking change and so you want to go back and use Node eight, luckily, there's a simple way to do this. There is a tool called NVM that's going to make this happen. Now let me walk through Node Version Manager just for a minute and you get to it, it's out on GitHub, if you search GitHub for NVM or can just go to creationixnvm, that's the right way to get there, this is a tool that allows you to manipulate and change the version of Node that you're using and if we scroll down, one thing that's very important, I'm going to scroll down to important notes right here. I am on a Mac, so I will be using NVM. NVM does not support Windows and that's very important and so immediately if you're running Windows, you may have just tuned me out and said, "Well, that's horrible." There are options, however, I have a Windows machine too, I use both and so NVM Windows is the tool that you will use if you're using a Mac. It's different but for the most part, in the important ways it's identical, so I'm going to kind of walk through NVM but if you're on Windows, use NVM Windows, it works pretty much the same way. So if you're on a Mac, click back over to NVM. If you're on Windows, stay over there but the first step is to install and so we'll come up and there's an install script and so if you're on a Mac, copy this right here and install it. On Windows, you can actually download if you scroll down, you can just download it right here. Once you have it installed, let's pop open a terminal window and let's look at what this looks like. So right now, I have 8.9.4 installed and that's the version that I'm using. If I type nvm install 9.5 and hit enter, it's going to install Node 9.5 and it's going to upgrade my version of NPM so now if I type Node minus v, I get 9.5. So that's great, I can switch back and forth. If I go back to nvm use 8.9.4, it switches back and forth and so that's the beauty of NVM is I can go back and forth between the two. There is one thing to pay attention to, though. If I go node use 9.5, now I'm using nine five. If I close this terminal window and open a new one, I go node minus v, I'm back to 8.9.4. So it's going to remember 8.9.4 as the default. When you do node use it's only for that one specific instance. One other thing I want to point out is there are some shortcuts. I can do nvm install minus minus lts and that will install for you the latest version and I've already got it installed but the latest lts version of Node that's available to you. Now the way to deal with the fact that things change is we can do nvm alias default, let's set it to 9.5, now when I close my terminal window and I open a new one again, and I do a node minus v, I'm using 9.5 and so that's how we deal with the fact that it's always switches back and forth to the one that's set to default. We just do this node alias default 9.5 and what I'm going to do is 8.9.4. I'm going to set that as my default and that's the version we're going to use for the rest of this course.
Command Line Node
Picking an IDE
Node Package Manager
Alright, before we get started, I know I keep telling you, "Before we get started." Well we got one more thing to talk about and then we'll start getting some code written. The first thing you always do when you are going to create a Node application, is get your directory set up and get your NPM going and so what we're going to do, is I'm going to make a directory called library. We're going to create an app that deals with books and books we've read and things like that. So I'm going to make a directory called library. I'm going to cd into that and then I'm going to run a command, which is the command that you should always, always start your project with, npm init and it's going to ask you for some defaults. You know, with a name, the version, a little description of your app, what you're going to start with. We're going to start with app dot js. No git repositories, no keywords, no author, no license and it's going to create this package dot json file for us and we're going to say yes. Now real quick I ran a command, npm init. Let me talk a little bit about what npm is so we can all kind of get on the same page. Just real quick, what is npm? Npm's just a package management for Node. So if you're dot net developer, think nuget, gems in Ruby, there's packages that you are going to use and Express is one of those packages we're going to use to build our web apps and we're going to use a ton of packages but that's what we're going to do and npm's a tool we do it and you may have heard of Yarn, which is the Facebook alternative, hopefully the idea is once npm improves, Yarn will kind of go away so I'm not going to show you Yarn but if you've heard of it, just know it's a thing that exists. It also is easy updates and version management and let me show you real quick what I mean by that. It handles the versioning of your packages very, very nicely. Alright, so if I open this directory up in Visual Studio Code, and I open my package dot json file, this is what we just had, right? So I've got name, version, all that stuff. One of the things I like about Visual Studio is if you hit control tilde, I get a little terminal window. This terminal window is the same as this terminal window, same thing. If I do an ls, or windows a dir, you can see I get a package dot json. So the very first thing, the second thing, write npm init, the second thing we're going to do is an npm install minus minus save express. We're going to hit enter and you will see now in my package dot json file, I now have express 4.15.2 as a dependency and it's just straight up this way. Now yours will have come with a caret in front of it. I've got it set up to not do that and I'll show you how to do that here in just a minute. But with this caret set up, that might be a little confusing or you might see this little tilde there, there's little symbols involved and so let's take a minute, let's talk about package management and see what's going on there. So I'm going to save that and then let's run through a couple slides, I'll come back and do a couple things.
Alright, so let's talk about NPM versioning for just a second because you get this little caret that says 4.13.3 and that's weird because you've got a symbol there but all this caret means is that it's going to automatically update for you as new package versions come out. So here I'm saying 4.13.3 but we know new versions of Express are out because we installed 4.15, right? So what the caret means is I'm going to keep the four and I don't care about anything else so that if I do an npm install, I'll get 4.14 or 4.15 or 4.16, whatever works and as long as it's the same major version, it's just going to take the latest. A tilde is a little more restrictive. So in this case, 4.13.3, if I have a tilde in front of it, then it's going to ignore just the minor version, right? So if I've got tilde 4.13, I don't care what's after that second dot, I'll take whatever as long as it's the latest. The last option and the option that my npm defaults to, is nothing, if I have nothing and I say 4.13.3, guess what? I'm getting 4.13.3 and it doesn't matter if new versions of packages come out or not, that's what I'm getting and a lot of times that's what we default to because there have been known to be, and there shouldn't be, but there has been known to be breaking changes in minor versions and so that kind of makes me squeamish and so I want to make sure I have a little bit more control. Some people are less concerned about that and that's totally cool. So you can do whichever one you want. That's kind of the way versioning works. Let me show you an example with Express so you see exactly what I'm talking about. Now here I've got 4.15.2 and if you go into node_modules, you can actually see this and remember all I did was nmp install express and look at all the stuff that came with it. These are all the dependencies for Express. Npm, the latest versions, have collapsed this. If you have watched this course in the past before the update, there was only one thing in there and now there's a whole bunch and that's just because npm has flattened out its dependency tree. So if I go to express and I go to the package dot json and scroll all the way to the bottom, you see I have version 4.15.2. If I come back to my package dot json and let's just say, hey, 4.13 and I put my caret there, save that. Control tilde to get my terminal and I'll do npm install. Actually let's delete the express directory here. Just so you'll believe me 'cause it's going to give me exactly the same thing, right? Npm install, I'm looking for caret 4.13 so it should've installed the latest version four so if I come to express, go to my package dot json, down at the bottom, 4.15.2. Come back to my package dot json, if I go to a tilde 4.13.2 and I run npm install again, now I should have four dot 13 dot something. It helps if I save it then I run it. There we go, now you see a whole bunch of stuff was installed and look express 4.13.4 was installed. If I take the tilde out, run it again, now you see 4.13.2 is installed. So in this case, we're going to do 4.15.2, that's what we're going to use for this course. Five is in alpha, there are not a lot of breaking changes, there's actually no breaking changes that we will use in this course so if it's express five that gets installed, you can go ahead and run this course with express five, that'll work. If anything comes up, I'll let you know in the comments and we can get that figured out. Now there's two things I want you to set with npm to get the same behavior that I've got going on in mine. We're going to talk about our npmrc file. So if you go into docs dot npmjs dot com, files npmrc, I'm showing you the webpage instead of putting this on a nice, pretty slide because I want you to reference the page and come out here and look because there's a bunch of stuff out here that potentially will impact what you're going to do. But what I'm going to do is I'm going to create a per-user config file, I've got one already created in my home directory that does a couple of things and come out here and look and get one set up for yourself and let me show you what that looks like. Okay, so from here I'm going to do vi tilde slash dot npmrc, just so you can see it. Okay, so I've got three settings set up here. Progress equals false, save equals true, and save exact equals true and the two that I want you to care about are save true and save exact equals true. If this is set to false, you're going to get a caret and remember I didn't get a caret and save equals true, let me show you what this does. So let me get out of that and come back over here. I'm going to do npm install chalk and chalk's the next one we'll install. Notice how I didn't do minus minus save and it shows up in dependencies automatically 'cause having dependencies is important and we always forget to do minus minus save. So now I just don't need to worry about it. I also got the exact version, 1.1.3. So that's what those two things are going to do for you if you set up your npmrc file. So get that set up for you. Follows the docs for depending on whatever OS you're on and we'll get that running. That's it for this. Now we've kind of danced around this for a little while. Let's go write a webpage. That's the next module, let's go get a webpage written.
Setting up Express
Let's start by getting our first web app up and running. Let's just get Express set up and see what's involved in getting that done. Here I've got my package.JSON file and that's all we have so far in our library directory. We've started this in the last module, we've got Express and chalk as dependencies and that's it. And so, I'm going to right-click in here and I'm going to create a new file. And we're going to call it app.js and you can really call this anything but app.js or server.js seem to be the standard that we go by. App.js is what we're going to call it. And then, there's a couple of things we've got to do right off the bat. VAR Express equals require Express gets us started, that gets us Express. And in node, we use the common Js pattern, so var Express equals require Express. We don't do the modules, the ES modules that you might see in other things. Require instead of import, so var express equals require Express. Now, we need an instance of Express too. We'll do var app equals Express and we're going to execute it. You may be tempted to come up here and shortcut this and just do that, don't do that, we'll need Express later. It'll be a couple modules from now but we'll need Express to pull routing and some other things in. So, leave it like this and we'll go from there. I like to leave a space here because this is going to be our list of actual requires and this is going to be the stuff that I'm doing outside of the require, so it's just my own personal thing. Now, the way Express works, is everything works through app. I'm going to say app.get and we'll talk a lot about what this means in a later module but for right now, just go with it. But think about get as, you've got a browser and you send a get request, to slash, and when Express gets a get request, to slash it's going to execute for us a function. That's it, that's really all Express is. It's just a series of, "Hey, when you get a request "to this route in this verb, execute this function." That's not too hard and when it executes this function it's going to send us two parameters. It's going to send us a request and a response. We typically, in the node world, just abbreviate those to req and res. You don't have to, you can call them request and response but that seems like a lot of typing that you don't need. You however, can't shorten them any further than that, it can't be R and R or re and re, so we get req and res. Now, when those come in, we need to send a response. So, we get access to the request and we don't care about that right now, we'll care about that later but for right now, we just need to deal with the response, so we're going to do a res.send and then we're just going to put a message in here. Let's say, Hello from my library app. Whenever I send a get, to slash, express is going to send back a message, hello from my library app. It's just going to send text, that's all it's going to do. And then the last thing we have to do to get express, like running, is for it to listen on a port. We just say, app.listen, and we tell it to listen on a port. In this case, we'll just say 3,000, that's pretty standard or 3,000, 5,000, you can really do anything, but 3,000 is what you'll see most of the time. And then when it starts listening, it'll execute a callback and actually I like this from Visual Studio, 'cause it'll actually even say right here, see, listen, the port number and a host name with a string and then a callback. We don't care about the host name, almost ever, but we care about the callback and so, we can actually just skip that and just go right to the function. You'll see it adjusts the little helper I get to say, hey callback function. And in this case, we'll just do a console log. Let's just say, listening on port 3,000, that's it. That's a full-blown Express app. We're listening on a port, we're going to respond to a get request, and that's it. Now, don't stop yet, there's a whole lot more to this but that's the core of what an express app really is. Let's pop over and run this so we can see what's happening.
What we're going to do now, is look at debugging just a little bit. And I'm going to give you three packages that we're going to install and use to work through debugging our application and just paying attention to the types of things that go on inside our app. The first thing we're going to do, if we break back out to our application is, notice we've got a console.log here that says, listening on port 3,000. And I teased you a little bit in the last module because I had you go ahead and install chalk and if we look at our package on JSON, there's Chalk version 1.1.3. If we go back into our app, up here at the top, we're going to do, var chalk equals require chalk. And what chalk does for us, if we come back over here and we run our application. So, node app.js, and we run our application, see we've got this, listening on port 3,000, and that's totally cool and it's helpful but when we start spitting out a lot of messages, it's hard to see through the noise, and so Chalk kind of helps us by allowing us to set colors on our error messages or our console messages. So, let's take this 3,000. I'm just going to cut that like this. We're going to add a plus. Chalk.green. And actually what's cool here in Visual Studio code, I can hit chalk., and we can see all the color options that we have. But I'm going to use chalk.green and put 3,000 back in there, then we'll save that, we'll flip it over, close it and start it again and now you see, hey we've got some colors going on. Chalk has this nice way of just popping out a little bit of color so that you can, if you have an error, you can color it red, if you've got something working, you can color it green, it just kind of helps you see what's going on with your website. One thing I want to talk about real quick, and we'll drop these in, over the course of this course, since we are in node and we're in node seven, we have access to all of the cool ES6 features. And so what we're going to do, is instead of this plus stuff that we got going on, we're going to use template strings. We're going to take all this off and at the end here, we're going to put template strings. Template strings have the little back tick and that's above your tab key on the Mac. If you're on Windows, don't worry it's in the same place, it's right above the tab key. Here we can just do, listening on port and then around chalk we just do this dollar curly brace, and then we close the curly brace, right there. Or we can save that, come back over here, close this, start it up again, we have the same thing. This is just a cool little way of embedding things in our strings and we'll use this quite a bit, but for right now just say, "Hey, that's cool, "we did string templates," and then we move on. The next thing we're going to do is get rid of console.log altogether, which is super cool because, really we don't want console.log. And I'll give you a better way. We are going to use something called debug. Var debug equals require debug and one feature of debug, the first thing we're going to do with it, is we're going to tell it where we are. So, we're just going to say we're in app and we're going to save that. Now, real quick, we've got to pop back over and do an NPM install. And because we did our cool little NPM hacks, we just have to do NPM install debug, and we're going to get 2.6.6 if we drop down to package.json 2.6.6. Now one thing I haven't talked about, but we probably should talk about just real quick is, if things are broken and we're way out of whack, you can do NPM install debug at 2.6.6 and hit go, and it will make sure it installs that specific version. If you need to do that, I will drop a note in the comments but for the most part, just go with what you get. Now that we've got debug installed, let's look at what that look like. Let's come back over to app.js and instead of console.log, we're going to type debug, and we're going to save this. If we come over here and we run this again, node app.js, notice we get nothing. Our message went away, which may or may not be a good thing but here's why. The cool difference from debug to console.log is that debug only runs when you're running in debug mode. So, if you deploy something in production and you're using debug, it's not going to spit stuff out of the console. That's awesome and that's a huge plus. The way you make it work though, is you type debug equals star, node app.js, and you hit enter. Note that on Windows, you've got to do, set, debug equal star, ampersand, node app.js. Look at all this stuff, oh my goodness. None of this stuff was here before. That's because Express uses debug too, and you can see the cool part about this is, look at this last message. App listening on port 3,000. The first word in all of these things is, what we passed in to debug when we loaded it. So, here we said, require debug app. App is listening on port 3,000, Express application and Express router are also spitting things out, which is super cool but this can actually get a little bit wordy. So, let's talk about how to fix that. You can come in here and just do debug equals app and now you're just going to get messages for app. So, debug is kind of cool because it's going to give us a lot of extra options when we do things. Let's do one more. We're going to do an NPM install, Morgan. And what Morgan's going to do for us, is log some things to the console that have to do with our web traffic. So, I don't have to write these out to the console on my own, we can just use Morgan to do it. So, we come back up here. Var Morgan equals require Morgan. Now, right after we do app, we're going to make a space 'cause we're going to do quite a few of these over the course of this, actually, this module but we're going to do what's called middleware and we haven't talked about middleware yet, we don't talk about middleware actually for a couple of modules but just bear with me, just know this is what we're doing. We're going to do app.use(Morgan). And we're going to say, combined, and I'll talk to you about what that means here in just a second, but just go with this. Some people will actually do this. I am not a fan of this, when we get down here because I like having all of my packages that I'm using just listed up here at the top of my application. If I have requires kind of embedded all down through, it makes it a little bit harder for me to know what's being used. So, just know, you might see that done some and why I don't do it, is 'cause I like to have everything listed up above. Now with that there, let's come back over here and let's start our application. Now, if we come out here, we go localhost 3,000. You see, hello from my library app, and then if we come back over here, you see I have a get, spit out to our page. There's a lot of information in there, which you probably don't care about, so let's fix that. Morgan has a lot of settings and we use combined, that's kind of gives me everything. We can do tiny if you want less information. We'll close this and restart it. Pull our page back up, hit refresh. Now you see we've got our get, and then a fav icon, and we'll talk about fav icon later. Just know that this is the icon that gets stuck up here in the top, and so, every time you hit a page, you get this fav icon thing and I'll show you how to deal with that a little bit later on. Notice, I've got a little cloud that's because it's some other localhost stuff I had. I had that little cloud as my icon, and since its quorum, do we get a new one, it just stuck that one up there. You probably won't have a little cloud up there, you might have something else and I'll show you how to get something like the cloud in just a little while. So, that's debugging. We looked at chalk, we looked at debug, we looked at Morgan and I gave you those three things now because now when we go and do index.HTML, and we start loading all these other things, I want you to be able to see what's going on inside your application when things start to not work.
It's one thing to be able to send some text back to a browser but we're building a web app here, right so we've got to serve some HTML. The rest of this module, we're going to build out and display an index.html page with bootstrap and font awesome and all this cool stuff. But let's start by just getting an HTML page served up. If we come back here, let's right-click in our directory and create a new folder called, views. And views is typically, where we're going to store all of our HTML files, or we're going to use EJS and pug here in just a little while, in the next couple of modules, is where you'll learn about what those mean. If I right-click in views and I do index.HTML, now I've got this HTML file. Let's just do HTML, body, and then we'll just put a message in here serving up some HTML, so that we know, hey, we've got an HTML file coming and if it's done right, we just get that message. The way we're going to do this is, instead of res.send, we're going to do a, res.sendfile. And what send file needs, is a directory and a file. And the way in node, you have to send a fully qualified path name to that file which can become painful, so, the way we do this in node, is we say, under under dirname. And what under under dirname is, is the location of the current executable. So, app.js and we do node app.js, under under dirname is where this goes. And then you can append to that, slash views. The problem with this, so, slash views, slash index.HTML. And there's a couple of problems with this. If I do this, if I leave the slash off, I'll just give you the basic problem. Really, to serve HTML that's kind of it. If I control+C and then I run this, and I pull my browser back up and I hit enter, we get an error. You see, library views is all concatenated as one word and the reason for that is because, I didn't hit a slash here. And that can become just painful, especially when I'm trying to concatenate a whole bunch of stuff together. What we use in node to deal with this, but I also remembered, it's cross-platform, so we've got different stuff over on the Windows side, so if you're running this on Windows, you'd have your slashes like this, right, that's a pretty fundamental difference. And for the most part, it can figure some of that stuff out on its own, but we use a package, called path. And path is built-in, we don't have to NPM install that, which is nice, the one less thing to deal with. But what path gives us is this method, called join. And what path.join is going to do, is join these strings together to form a valid name. Now remember, I got in trouble when I did this. Path.join is going to fix that for us. Now, I've seen some code, where I get path.join and then this still, plus stuff going on, and that's kind of defeats the purpose of path.join. Instead of a plus, we just send in a series of params. So, dirname and then views index.HTML, and it'll join those together for us. Then we hit save, I kill this, I run it again, I refresh my page, and now I get my serving up HTML. So hey, that's progress. Just to kind of prove my point on why path.join is cool. If we do this, notice, there's no slashes anywhere. It still works. One other option is look, I'm going to do slash, and I'm going to do slash, and I'm going to do slash, and I'm going to do slash. So, now it's going to be double slashes there, guess what? It doesn't care, it just works. We'll talk more about path, here actually just in a little bit, when we start talking about static files, but for right now, we've got an index.HTML files being served up. It doesn't do much, but at least we've got one being served. Let's start talking about some style issues. I want to start building out our index but we're going to use bootstrap, and we're going to use font awesome and we're going to use a couple other things, and we need to get those installed before I can get too far into building out my HTML file. In this next clip, I'm going to show you a few different ways to make that happen.
Static Files - CDN
Static Files - Public Directory
Static Files - Node Modules
We've got index.HTML up and running. We've set up express, Express is running, it's working, we're serving a slash HTML and we've also spent some time logging, we've got debug set up, we've got Morgan going, we've got chalk to make our messages pop out a little bit more. Serving our HTML file, our HTML file doesn't do much yet. It just says, we are bootstrapping, and that's it. We've got our static files served, bootstrap, jQuery, coming directly from node modules. We looked at CDNs, we looked at copying things over to our static files. And now we're serving them directly out of node module, so, we still have some NPM ability to update things as needed. That's what we've got and let's now, take a break for a second and let's go work on some tooling. There's a couple of things going on right now that I don't like. One is, every time we make a change to our Express files, we've got to restart our server, I'd like to do that automatically. We're hard coding some things, we don't have anything in our environment variables. I'd like to put some stuff in environment. I'd like to be linting our code, so, let's take a break from building out the website and go spend a module working on tooling so we can get our environment working the way we want it.
Setting up Tooling
Alright, so let's take a look real quick on how to set up some NPM scripts and how to start our application in maybe an easier way. So if we come back into Visual Studio Code here, we've got our application open, we've got out app.js, and to run this we open up our terminal window and we can open up an external window but I want to do it here just so you can see what's going on. So the way you open this, if I do control tilde, no matter what platform you're on, it's control tilde, I get this window down here and I can slide it up so we can see what's going on. The benefit of using this window is when I hit control tilde, I get a terminal prompt at the exact directory location that I need it at. So that's a huge benefit for us. You don't have to cd into anything, it just kind of works and so when I do this, to run my application, I'm supposed to type node app.js and I run that and now my application's running. Now remember I don't get any output because, so control c to kill that, we were looking at debug. So I've got to do debug equals star node app.js. Now remember if you're running on Windows, we're going to do set debug star and then an ampersand there. That's the only difference, so just remember if you're on Windows, you need this ampersand and that set every time that I type this. Okay, so I run this, now I get everything. I get this huge output and realistically, like we said before, we don't want all of it, right? So I'll control c, I want app. Or I want, you know, whatever we want. I'm going to run that, so the problem with this scenario is that it's painful for a person who's got to come along behind you or even you, like two days later, to remember what it was you wanted run when you run. So what specifically you want to type and so what we want to do is set up a scenario where you don't have to worry about it and when somebody else goes and downloads your code, they don't have to worry about it. They just enter the same command every time and that brings us to scripts. So if we go to your package dot json file, I'm going to close this window. You see right here we have a scripts section and what the scripts section right now has in it is test. So I pop this back open, control tilde, and I do npm test and I run that, you see right here it says error colon no test specified and where that comes from, if you look up above, test is going to echo out error no test specified and then exit with a one, which is an error condition. So that's how that works and there's two built in. There's test, I can just run npm test, or I can even run npm t for those of us who are lazy, or I can come up here and we have start. Now notice Visual Studio Code is kind of awesome and this is why I like it. We're not going to talk about all of these but here's a whole bunch of other ones that people tend to use. We'll talk about the pre stuff here in just a minute but I'm going to do start and when we start, we want to run to debug equals app node app.js. Now if this isn't working for you on Windows, remember ampersand and set, don't forget about that. That's the last time I'm going to remind you, though, 'cause now you know. This is working, I save it, I come down here, I do npm start and it's going to start my application for me. Back over to app,js, you see listening on port 3,000, listening on port 3,000. That's the npm scripts, that's their purpose because now when you go pull this application down, you just type npm start, nine times out of 10, whenever you download something off of GitHub or you go pull a project somewhere, you type two things, npm install and then npm start and you're running, so that's start, that gets us going, we now have a running thing. Let's talk about adding some more things to this list of scripts so that we start having a more robust application. So we're going to start by the conversation of ESLint.
AirBNB Style Guide
Alright, now that we've got it installed and we've got it configured, basic configuration, let's run it and see how that works. So the easiest and simplest way to run this is just to type in our terminal window, and I'll make this bigger, you click on the caret, make it bigger, it's just to type eslint and the file, app.js in our case. Notice how it breaks with this error about not being able to find the package. So let's clear that out and let's talk about one issue that we have with ESLint, now that we have some of these local packages installed. Config airbnb and plugin import, is you can't use the global anymore. We can use the global to run basic ESLint but once we have packages installed, we have to install the packages globally or it can't find them and so that doesn't work for us So what we're going to need to do is run the local version that we just installed a second ago and the way we do that is dot slash, if you're Windows, you know, node_modules and inside a dot bin directory, inside node_modules, is going to be a, so if we come over to node_modules, inside a bin directory, we're going to have this eslint right here and that's what we're going to run, eslint app dot js. Now I'm going to maximize this so you can see it run and when we run it, look, I got a whole ton of stuff. So I got unexpected var, use let or const, we'll talk about that, spacing issues, all of these other things. But before we dig into start fixing these, let's talk about this whole global versus local thing.
Global vs. Local
So what ESLint has done is exposed an issue that we have just in general with global installations of NPM so let's take just a second and talk about what that means. So if we come back over here, I opened back up our little window. In order to run ESLint, when I type eslint, if I look at the version of this, I have version 4.10. Now if I start a new project and I want to use a newer version of ESLint, or maybe a project that's out there that I'm working on has an older version of ESLint, I'm stuck with 4.10, I can't change that because it's globally installed and so that's one of the downsides of globally installing it. It is possible to run it locally, right? We did dot slash node_modules dot bin eslint minus v, we can run it that way but that's painful, right? I mean, that kind of stinks. So we don't really like that idea either. So that's where scripts can come in handy and to prove my point, I'm actually just going to uninstall because we don't want to do this ever. So I'm just going to run an npm uninstall minus g eslint. Now it's gone, so if I run my eslint minus v, I get no such file in directory, alright. So let's kill this, if I come back over to my package dot json, I come up to my scripts, I can actually, right here, create a new script and I showed you the list and this isn't going to be in the list of standard stuff. We're going to create our own thing, called lint and what lint's going to do is it's going to run eslint app dot js and when you think about it, this makes sense that if I installed this on a production server somewhere or a CI server somewhere, I don't want my CI server to have to install things globally like that just doesn't even make sense. Hey, you know, Heroku, in order to run my app, you've got to globally install stuff. So what npm does with scripts, is it assumes a local version. So when I run npm run, now anything's that not test or start, test and start are unique, if it's something other than test or start you actually have to type run, npm run, hey npm run, this script called lint and it's going to look at my local node_modules and it's going to see what's installed locally and then use that version to run. Okay, now you see everything has run and we're working, npm run lint, that gets it going and now we need to start fixing stuff and you look at, though, the first section, expected var let's use let or const, Let's talk about ES6 for just a second because several of these are ES6 and so I want to give you just a brief little ES6 thing and then we'll talk about moving everything to ES6.
Refactoring Tips in VS Code
While we have the opportunity to do so, I want to talk you through just a couple of tips on VS Code and some things that you can do to make your refactoring experience a little bit easier and better. Just a couple real quick and so I'll show them to you as we're cleaning up everything that we're doing. Alright, so, you'll notice first, the first thing we want to fix is all these vars. So here I've got a whole bunch of vars, you may want to change your, so this name app, see there's a whole bunch of them, we may want to change var and when I double click on it, notice when I did app, see all the apps lit up. When I double click on var, all the vars light up. If you've got variable names, you might want to change all of the instances of that variable name and that becomes very easy 'cause I can just double click and instead of, you know, normally what you do is control F, you know, search replace, control shift F, to search everywhere, you know, all of that, we don't care about any of that, right? I want to double click and I can do command function F2 and what command function F2, or on Windows control function F2, what that does for you is notice now all of my vars have a cursor in front of them and what I can do now is I can just hit the backspace key and I'm changing them all, all at one time. So anytime I want to change something, all I have to do is double click on it. So if I wanted to change what express was, I can hit command function F2 and I can start changing. So that's an easy one and if I run this again, those are now gone, I don't have those anymore but I do have a lot of formatting kind of stuff. A space is required after kind of things. So let's fix that. If I right click, I can do format document. Just boom, just like that and I'll undo it, so you notice I've got no spaces, things just aren't quite right, I can right click format document and it's going to format and give me extra spacing right here, these spaces here, save that. If I run this again, now you'll see those are kind of cleaned up so once you've typed a whole bunch of stuff, just, you know, right click format document, you're done. You may notice, this down here could potentially change a whole bunch of stuff. So if I've got something like this. I've got a tab here, if I run this, I'm going to get an error that says expecting indentation of two spaces but found one tab. Well if you're a tab or a spaces person, it doesn't really matter, I prefer tab, this is where I tab, I don't want to space twice but some people are all offended about spaces and this particular ESLint package wants two spaces so that's cool, what you can do is you can click on this down here. Indent using spaces and, hey, I'm going to do two. It automatically changes this to be a size of two but it's still a tab, like if I just push back and forth. If I hit the backspace and hit tab now, since I'm tabbing with spaces, it's going to input two spaces for me. So it's going to make all my tabs spaces just by default. So I don't have to worry about it. So I'm going to, now notice, we're down to just a couple left and what this is, is all about arrow functions. So notice right here, unexpected function expression prefer arrow callback. And what prefer arrow callback means is whenever you're doing a callback, if you're doing a function, that's fine, make it a function but whenever you're doing a callback, it would prefer that it be an arrow function and it prefers that for a couple of reasons. One that this keyword binding in arrow works kind of the way you'd expect. Instead of the broken way, broken in air quotes, way that functions do and we'll talk about that later when we have a need for it but for right now we don't but it's just, that's the gotcha and so it just prevents you from ever having to deal with that 'cause there's no negative to using an arrow function. If I have this here, there's no downside to that and also I can just do this. I can get rid of everything, get rid of my curlies because I don't have to return anything. Now it just works but I'm going to pull this all back to like this because I want to show you one other thing. 'Cause I've got two of 'em here, I missing a semicolon here, there's all kinds of weird stuff broken. I can come down into this window and I can type dot slash node_modules dot bin eslint and the reason why I'm doing this is because I don't want to change my npm scripts to do this. I'm just going to run it and you'll notice right here it says four errors, zero warnings, potentially fixable with the dash dash fix option. ESLint, once you become comfortable with it and you know what it's looking for and you know what it's going to do, you can run it, so app.js with the dash dash fix and when I run this, notice, look at lines 15 and 19, when I run this, it changes them, it converted them to arrow functions, it also added a semicolon right there. That is essentially magic, really. Just, hey, here's some problems but guess what, I'm going to fix them for ya and now everything's going to work. So now, now that that's done, I'll run my npm run lint. Boom, everything works. Now that may all seem fairly routine, right? So it complained about spaces and it didn't complain about const versus var and all those things and that seems fairly mundane, right? It's not really doing us a whole lot of good yet. Although, that's important things. The type of stuff that it is going to do for you, though, that I want to make sure we talk about is notice where const chalk equals require chalk. If I come into package dot json and remove the reference to chalk, basically what that means is moving forward, if somebody pulls down this code and does an npm install, it's not going to install chalk. It's going to work for you, but it's not working anywhere else. Well, now you'll notice, I've got a red squiggly underneath require chalk and ESLint is saying, "Hey, you're using something that doesn't exist." And that's important and that's going to protect you from potential problems. It's going to work on my machine because I have chalk installed already before I removed that package. So that's one thing that it's going to do for you. Another thing it does for you, there's a bunch, but right here, notice I do path dot join dirname and public, what a lot of people do, and I'll see this every once in awhile, is they'll do that, that should work but sometimes, right? And not on all platforms and so now I get an underscore, squiggly, that says, "Hey, no path concat. "You can't do that or you shouldn't do that here." It's just going to prevent you from making some mistakes that you might make otherwise. Alright, let's round this module out with some examples on how to restart your application automatically and then maybe pull in some environmental variables as well.
Setting up Nodemon
So the last tool we're going to set up before we really start going on our application is Nodemon and the reason we need Nodemon, if we look at the website real quick, is Nodemon reloads your application automatically. It's a simple utility that's going to monitor your application and when you make code changes, it's going to automatically restart your server for you and it's just an easy way to not have to remember to stop and restart every time you change something. So what we're going to do, notice it says the first thing npm install minus g nodemon and I have a fundamental dislike, as we've said already, of doing a minus g install so we're going to just do an npm install. So we're going to pull up our terminal window here and just a reminder I did that with control tilde or you can go to view, integrated terminal right here and that'll open it or close it. Okay, so what we're going to do is an npm install nodemon and that's going to get Nodemon installed and set up for us and I'll pull this back down while that goes and we're going to change our start script to be nodemon. The next thing we need to do is set it up and we can do to the documentation. I'm not going to walk you through all the documentation but I'm just going to give you a basic idea. Notice it's nodemon your app and we can config by using some basic configuration in our package dot json, so check this out right here. We like that. Okay, so what we're going to do is come into our package dot json. Notice we now have nodemon down here. Add a comma and we are going to type our nodemonConfig right here and what that's going to do for us is just tell Nodemon how things are going to work and we're going to keep this simple. We can make this a lot more complicated later if we wanted to but right now I just want to keep it simple. So there's a couple things we need to put in here. The first one is restartable and here we just say, hey, if I to hit rs, it's going to restart automatically. I'm also going to say, hey, we need some ignore options. Now if we come up here, if I change app.js or anything in our source directories, I want it to restart but, you know, if we do an npm update or we do an npmsr or anything changes in node_modules or things like that, I don't really care about that. So ignore is going to take an array of things we don't care about and so we're going to add node_modules to that. If I make a lot of changes really fast, I don't want it to restart every time a file changes. So we're going to set up a delay of 2,500 milliseconds or about two and a half seconds and we're going to save that. Now we changed right up here, our start to be nodemon app.js. Now if you're kind of just jumping in here, remember if you're on Windows, you'll need a set right there and an ampersand, I'll probably remind ya again even though I'm going to say I'm not going to remind you again, but get that debug app nodemon app.js right there. So let's come down here and do an npm start. We'll hit enter, now notice, we now have nodemon running. To watch, to restart anytime, type rs. Guess what, I'm going to type rs. There we go, we restart. I also can come into here into app.js and if I just change my on to at, just something simple, I can save that and see after a little pause, it restarts. Two and a half seconds, there we go. Alright, so there's nodemon set up. That's just going to keep us going so we don't have to stop and start all the time. Nodemon also provides one other excellent thing for us. Notice I have app dot listen 3,000. I have hardcoded my port, which is not something we want to do and I want to be able to pass in to the application from my process, from my environment, what ports I want to use. Nodemon lets me do that and all I have to do is come back over to my package dot json file, we'll minimize this, come back over here and add an environment. So notice I have port 4,000 now. NODE_ENV development, so I can set my node environment, I can set my port, I can set whatever I want in here and we'll add some things into this later, like database locations and things like that. So we're going to save that, come into my app.js. Now up here at the top, let's do this. Let's just pull our port in, so I'm going to say const port equals and I'm want to pull in my process environmental variables and we're going to actually call that process dot env and this where, basically process dot env is just going to be an object, it's a big object of all the things that I've passed in. So what did we pass in? Port 4,000, so I'm going to do dot port, so port is now going to be either process dot env dot port or what if that's not set? What if something's broken? Or 3,000, now I get my red underline because port's assigned a value but it's never used so this eslint telling us that we're doing something. So now it's used that so I'm going to come down here and I'm going to say port and port. I'm going to save it, now notice it's still listening on port 3,000. Well the reason for that is I added this stuff in here but Nodemon doesn't know that 'cause it hasn't read its config. So in this case, even though I just teased you with all that, in this case, I still have to restart it. Notice now, I'm listening on port 4,000.
Alright, we've covered all of our tooling that we needed to get set up and get going in this module. We started with NPM start, just I can write an npm start, our application starts, I don't need to worry about anything else. We talked about ESLint and static code analysis and getting all of that set up and running. ES6, we started using arrow functions and we used string templates and all those types of things to kind of add a modern feel to our application and we'll continue to add more and more of these ES6 and ES2017 and so on syntaxes as we go throughout this course. We set up Nodemon, it's going to monitor our files for us and do restarts and get all that going. We also closed with some environmental variables. All that's set up now, we've got our application running, let's go write some code, let's go get this application going so that we can start making something that's useful to us.
Welcome to our Templating Engines module for building web apps with Node.js in Express and templating engines are the thing we're going to use to build out our HTML, kind of the most important part of a web application is the part you get to see. Now we're working through this course, that's building out a full blown web application, we've got our Express server up, we've got our tooling set up, now let's start actually building out our web application. We're going to start with this conversation with templating engines and templating engines are basically the thing that Express uses to generate our HTML pages, we're not going to write a bunch of HTML and just serve it up statically, like we've been doing up till now, we're actually going to generate our HTML dynamically, so that we can include our, you know, variables and things from the database and all that. Now there's two almost extremes, when it comes to templating engines and I'm going to talk about the two extremes, I'm going to give you an example from each and then you can decide for yourself which one fits better with your style. The first one is Pug, now Pug used to be called Jade and they've changed its name to Pug and Pug is very much a white space driven, minimalist HTML type engine, that allows you to greatly simplify what you're typing and what you're putting in, it just generates a lot of it for you, so that's one extreme and we're going to use that for a couple of clips and I'll show you how that works. The other extreme's going to be EJS and EJS basically just takes HTML and drops tags in it for variables to be plugged in, very similar to how you would do in Angular or React, you know, we're just going to create HTML and drop some tags in there. By the end of this module, we're going to have a real page up and running and so that'll get us pretty far, we're going to use EJS for the full course, but I want to show you Pug, just so you can experience it, but then by the end of this module, we're going to have a full blown, real page up and running, that's going to look the way we want it to look, we'll get some CSS going and all of that and then we'll go from there. So let's take a look at Pug first.
We are slowly but surely making progress in building a web application with Node and Express, we finally have something that looks like a real web page and it's only taken us four modules to get there, but in this module, we talked templating engines and this is the thing that Express uses to throw some HTML out to the browser. We started by using Pug and Pug, or Jade is the other name it used to be known by is a minimalist kind of looking template, that pulls put all of the extras and it builds HTML for you. Now we talked about EJS, which is the other extreme, which is just HTML with some tags thrown in, we're going to use EJS for the rest of this course, just because I think it's slightly easier for everybody to wrap their heads around, while we're talking about all the complications on the backend and building up our Express web server as we go. We built our first real page, we pulled a template down from Bootstrap Zero, we plugged it in, we added some EJS tags and now we've got a real page up and running, now let's actually start building out our whole application using some routing, we'll pull some databases in, we'll do all of that stuff and we'll get a real application going.
So we're working our way through building out a web application and in this module we're going to start talking about routing and we're going to build out some routes for our application. Now we've come pretty far in our journey to build out a web application. We've got some tooling set up, we've got basics of a webpage showing, we imported a template that gave us a nice looking webpage to get started with and in this module we're going to start building out routes, we're going to have a book route, we'll have a single book route. We'll kind of work through all that. Now we're going to be generating some code as we do that and so we're going to start talking about how to separate out our files. Right now everything's in one file, in app.js, and we're going to talk about how to pull that apart so we have multiple files. We're going to add some parameter variables to our search, like when we build out a single book, I want to pass a book ID, so how do we do that in our routes? And we're also going to talk about router functions. We're going to create our router but we're going to do it as a function so we can pass things in on the creation so we can create different things, or pass config variables in and things like that. So let's get started in building out our routes.
Alright, so we have in place some anchor text and some links that we want to go to. So if you're following along, you see we've got our books and our authors and those both go to local host 4,000 books, local host 4,000 authors and we now need to implement this, book route, and the way we're going to do that, there's a couple ways. So the first thing we can do is we can come down here to our app dot get and we can actually do app dot get slash books and just implement that right there and that works, that totally works, you can totally do that but that gets clogged very quickly and you end up with all this stuff in there so we're going to use this thing called a router. Basically what a router is is it allows you to encapsulate all of your routes in one spot. Let's go down below app and port and we'll just say const bookRouter equals express dot router and we're going to execute that and what that's going to give us is a thing that we can use to encapsulate all of our routes and obviously we need to spell express correctly and this is where ESLint comes in handy, right? I mean, there it goes, it tells me, "Hey, that thing doesn't exist." So express bookRouter has a problem because it is not used yet and we know it's not used yet so we're going to come down here right after this app dot get. We're going to come down here, actually let's do it up above because we're going to pull this out a little bit later. We're going to say bookRouter dot route and what this does is it allows us to set up a series of routes to associate with bookRouter and in this case, we're going to say slash books because that's the route we want and so everything that goes to slash books is going to use this bookRouter and then here we can do dot get, we do dot post, we can do all of those same things that we've got down here off of app. Okay, so let's implement our dot get and for the most part, most of our pages are going to be gets, we're going to have a few posts but most of the time, we're going to do gets and we're going to do this the same way we did our get down below, we're going to send in an arrow function and we're going to send in req and res. We'll format real quick, clear it up. Okay, inside here we're going to say, what do we do, let's do a res dot send, we'll just send back hello books and we'll be done. Now what this does for us is now it encapsulates this book route so when you go to slash books get, you're going to get a send of hello books. Now the next piece in this is to let the app know that we're using this router and so, see up here where we've got all these app dot use statements? It's the same thing so we're going to do app dot use, we're going to say, hey, for slash we're going to use bookRouter. Let's save that and then down at the bottom, we see it flip over, so we'll alt tab, refresh. Hey, we got our hello books. So that works and that's great but there's a couple different things we can do here and do to help fix this up. This is a little confusing because looking at this app dot use slash bookRouter, we don't know what routes are being maintained in bookRouter so what you can do is actually you can say, hey, anything that's slash books is going to be bookRouter and if you go to the root of bookRouter, then you're going to get hello books. If you go bookRouter dot route slash single, we just copy this dot get and stick it back here and say hello single book and we'll use this maybe when we do, when we look for just one book, so we'll change this to by ID a little bit later on. We'll save that and now when I go to slash books, that works, if I go to slash books slash single, I get my single book. This is encapsulating everything into a single router in a single place so that's great and then we've got our route set up but what that doesn't give us is something meaningful so let's add some pages that we can render so that if you're, if you go to slash books, you actually get a list of books or let's start rendering different pages in different places.
We've got our book router routing things appropriately but we don't have yet are additional pages and realistically when we go to books, we want it to look very similar to this 'cause really this is going to be our list of books. Eventually this page will become something different and we want books to look like this page. So let's make that happen. The first thing we're going to do is we'll slide this back over, is we're going to go into our views directory and we know it's views because up here on line 17, we did app dot set views dot source views. So that's where we're going. So index dot ejs is what we have. So we're going to create a new file called books dot ejs and realistically because we want the same thing, we're just going to copy this entire file, just copy it into books dot ejs and we'll save that and then in our app.js, minimize this again so we have more space, right here where we go bookRouter slash route, right now we're doing a res dot send, instead we're going to do a res dot render and we're going to render books and we're going to save that. Now when we save it, you'll see we restarted down here at the bottom. So we'll come back over here, we'll refresh this page and now we have an error, and we have an error, if you'll look at line number six, title is not defined, if you recall, we were sending in all this stuff on our res dot render. We were sending in our nav and our title down here. So let's copy that, come back up here to where we render books, paste that in. Do a format so it doesn't yell at us, there we go. Make it look pretty so it doesn't yell at us and then refresh our page and it's there, okay. So now on books, we have a list of books, except we don't have a list of books, right? We've got a list of fake articles that were included as a part of this template. So let's actually add some books and we'll stick those in and let's see how that works. So what we're going to do, is come back over to our code, is let's just create right up here a thing called books and it's going to be just an array of books. Now eventually we'll pull this out of a database but databases are the next module so we're not there yet, let's just hardcode some books and what I'm going to do is I'm just going to paste a whole bunch of stuff and then I'll format it so all my red lines go away and set it to const and there you go, now we've got our book. Const because it's not changing, not var, and so you can type this in or you can type in whatever books you want, it really doesn't matter. For right now we're just going to use title and author. Genre's going to come about later and whether or not we've read it will come about later but for right now, you can copy and paste this, you can pull it out of the course materials, if you pull those down but make sure we have books. Alright, now it's red underscore 'cause we're not using it yet so let's add it. If we come down here to where we're rendering books, right now we're sending in the nav, the title, and then we're going to send in books. Now we used to have to do books books and you'll see ESLint yells at me right away because we don't have to do that. Notice the error says expected property shorthand. We can just do that. So now I'm sending books into my render for books. So if I come over to books dot ejs, and we look for the nav, so if we scroll down to where we do the nav, you'll see we've got this for loop right here where we iterate over our nav. So I'm going to copy that for loop and we're going to scroll down. Let's look back over here. See repurpose content to reach a wider audience? So this is where it says div class equals row, that's where we want to copy that for loop. So let's paste that and close our for loop. Then we're going to copy this row. Let's just copy the whole row and we'll drop it in there. 'Cause this is what we want to loop over. Now if we look back at this, realistically what we want to do is this title, repurposing content to reach a wider audience, is going to be the title of the book. This 97thfloor.com is going to be the author. So let's just change that. Repurpose content to reach a wider audience, is going to be books sub i dot title because in app.js in our books array, that's what we called it, title and author, books and we're sending books in right here. So let's come back over books sub i dot title and I know we haven't changed up on the array yet but we will, hold on. Here where it says 97thfloor.com, we're going to do author. Now we're iterating over books so let's come back up here and change that books. Alright, we'll save that, now all these other rows we want to get rid of, row, row, row, row. Just delete 'em, they're gone. So now we're just going to loop over books. Let's save that, come back over, refresh our page and notice we've got War and Peace, Les Mis, Time Machine, all of our books looped over, just like that. So the next piece of this is come back over here, see this read more right here? Well that read more is actually kind of way over to the side. So let's scroll over, let's move this down to the next line so we can see everything, let's actually implement this to slash books slash the ID and we put the equals sign now because we're going to write out the value and we're just going to say i. Let's save that and refresh our page and now the read more, when you hover over it, notice it says books two or books one. Now we want to build out those single routes and so if we come back over here, here we've got our single route and here's our other route and all this stuff is starting to get a little bit crowded. Our app.js is already over a hundred lines of code and that's too much so as we're building out the single route, let's see how we can pull all this code out and drop it in a different file so that we have a cleaner and easier way to encapsulate all of our code.
Alright, so let's take a look at how to separate out all of our files into individual files for each thing that we do. Alright, so what we're going to do is let's take where we start with const books all the way down to bookRouter dot route single and we're going to just command x that, control x that. We're going to click here and we're going to right click on source, we're going to create a new folder called routes, we have a folder called views and now we have a folder called routes. We're going to create a new file called book routes dot js and we're going to paste all this stuff in here. Now we're going to need a couple of things. So if we come back up to the top, we're going to need express, we're going to need to format our document a little bit, and then if we look over here, we also need our bookRouter equals express dot router. So all of this stuff we've already had, right? So we just bookRouter our book router, book router, all that stuff is there. Now we just have to export it. So we just say module dot exports equals bookRouter and that's it, so all of this stuff, used to be in dot js and we just pulled it out and we don't even need this line anymore 'cause we need bookRouter from this, not from express dot router, so actually what we're going to do is now we're going to say require and now here's an important thing. Notice up here we have all these require statements and we just say path or morgan or debug and when you require them that way, it goes to node_modules. Instead we're going to say dot slash source slash routes slash bookRoutes. Now bookRouter is coming from this other file and so because we have the dot slash source, it's actually going to look in that place. So we'll save that, it's restarted already. There it goes. And there you go, it still works. So all it took was taking that chunk of code, moving it over here, to a new file called book routes, and then requiring it this way and what we're actually going to end up doing, I'll move this line down here, let's move this line down, so we can put them all together, as we have more routes, like we'll have auth routes when we start doing users, we'll have all of our routes lined up right here. Const bookRouter equals require. Const authRouter equals require and then we'll just app use right below it. So now we have two lines of code to pull in everything. That makes our lives quite a bit easier and it's easier to figure out what's going on.
Single Book Route
Alright, so now we're going to set up our route for just the single book. Now if you recall, when you hover over read more here, and we go to it, we get slash one or slash two or whatever the ID is of that book and so what we want to do is back in our book routes, when we get to slash single, we don't it to be slash single, we need it to be the ID of the book that's being passed in and the way that works is we put a colon here and then whatever we want to type. In this case we'll type ID because that's the variable name associated with what it is that's being passed in but what happens is when I do this, Express takes whatever is beyond that last slash and so in this case it's slash one and it sticks it in a variable name called ID but it's not directly in that ID. It's going to put it in req dot params dot id and that's going to be whatever it was that was beyond the slash and so what we're actually going to do is we're just going to say const id equals req dot params dot id and then we're going to take everything that's in this render, just take the whole thing. Here we're not going to render book, we haven't created that yet but we're going to render book and instead of books, we're going to say book sub id. Now, yes, we could have just said req dot params dot id right there but I'd rather pull everything I'm using out up at the top, that just looks a little bit cleaner. We don't need a semicolon on the end there. And then we go from there and this is interesting, notice I have a red underscore and this is one of those ES6 things that's kind of cool. It's saying, "Hey, use object destructuring." We're going to prefer destructuring and so let's do that. So we have this object called req dot params that has an ID on it and this essentially works exactly the same way. So it looks for, because I have these two curly braces, it looks for id inside req dot params and just pulls that out. So it's just a shortcut notation to get what we want. So we'll save that and now we're not going to look at it yet because I don't have book for my rendering yet. So let's go create a book view and go from there.
Rendering a Single Book
Alright, so what we're going to do now is we're going to figure out some dynamic ways to implement our router so that we don't have this nav hardcoded everywhere and so if we go to our bookRoutes dot js, see we've got nav here, we've got nav here, it's being reused everywhere and what I really want to do is I'm going to copy this nav, and what I really want to do is right here, I want to say const nav equals this array. Just like that and I want to then take this nav and I want to just pass it in to my require for book routes and remember, like, look up here, when we were debugging, when we were setting up debugging, and require debug and we pass in app so we pass in some configuration data and it uses that information to do what it needs to do. Now we're going to take the same concept down here in our book routes, our book router, and the way we're going to do this is we're going to turn our bookRoutes dot js into a function instead of what it is right now and you can pass in all kinds of stuff. We can pass in configuration information, we can pass in database stuff, you know, whatever we want to do, there's a hundred different things we can do but we got to talk about how to make that happen. So let's save this file, we'll pop over to book routes and let's minimize everything so we got it all on one page. So we want to keep the first two lines the same but then we want to say, we want to create a function called router and we are going to put everything in this file, except for our module dot exports, inside this function and then let's minimize everything again so you can see it all on one screen. Alright, so the only thing we need to change. We've copied everything up in here is right here on the bottom line, it's going to make me expand it before it'll let me do it, is we need to return book router because we're creating book router, now we just need to return book router out of our router and remember this router function takes nav, it takes a parameter in, now we need to use this nav. So if we come down here to bookRouter route slash, here instead of passing this nav in, we can just do that and in our slash id, instead of passing in our nav, we can just do this. And then the last thing we need to do is instead of exporting bookRouter, we need to export router. So let's save that, format our document to get all of our stuff out of the way. Save this whole file, okay, we'll come back over to app.js. Now let's change this just a little bit. So let's change book and let's change authors and we'll see how that looks. So we'll come over here, let's refresh our page and it didn't work and it didn't work because I changed the wrong thing. We want to change it here, alright, let's save that, refresh, and there you go, everything still works. So that's how you pass data in and implement this kind of dynamic setting up of your routes. Just kind of the same way we did up here with debug. Now the one thing that still bugs me that we got to fix right now is this book array. I don't like the hardcodeness of this and so we have reached the part of this course where we're going to go start talking about databases and we're going to start pulling this data out of a database and so that, we start to get pretty far along. Okay, so let's recap real quick and see how far we've come and then let's start implementing a database.
Now we've made quite a bit of progress in our journey to build out a web application in Node.js. In this module specifically, we started to build out routes and we discovered this router thing that Express has that lets us encapsulate up our routes into one object that then we can pass to app to use. We started separating out our files. We moved our router logic out to separate files so that not everything's all clogged up in app dot js. We started using parameter variables and we talked about how we have params attached to our request that allow us to pull in items from our URL to use and we ended with a conversation on router functions and how instead of returning the router, we can return a function that will return a router based upon some configuration information that gets passed onto us. So that's great, we've made great progress. Now it's time to start hooking things up to a database.
Alright, Now it's time to start talking about databases and how to get and put data into our application from a database. We're going to talk about a couple of different kinds of databases just because one size doesn't always fit all. We're working our way through building out a web application. At this point, we've got a couple of pages, we've got some data flowing through to a template engine. It looks kind of nice. But all the data is hard-coded. Now it's time to hook things up to a database. I know one size doesn't fit all on databases, so we're going to talk about two different types. Kind of like we talked about two different types of templates, we're going to talk about two different types of databases. The first one is going to be SQL Server. I'm intentionally doing SQL Server because it's usually not thought of as being something you connect to from Node. But all of the .NET developers out there who are moving to Node, or all the Microsoft kind of people, I'm going to show you, you can still use SQL Server from a Node application. I'm also going to do the more traditional Node database of MongoDB. So you'll see the difference between the two. Now don't freak out if you are using a Mac. I'm going to talk about SQL Server. In this course, I'm going to set up an Azure database and connect to it that way. I'll show you how to do that as well so that no matter what platform you're on, you can still connect to SQL Server. We're going to talk about the Mongo command line a little bit because that's going to be a little different. If you're used to more of a traditional SQL Server kind of thing, I'm going to show you the command line for Mongo. We're going to insert data. Obviously that's generally a good start. We're going to put data in the database, and we're going to pull data out of the database. Then I'm going to show you a couple of different query types. We're going to implement our find and our findOne. So by the end of this module, we're going to be pulling data out, and we're also going to be doing a search, and we're just going to pull out that one. We're going to do that single book route, so we can pull out just one. All right. Let's get started by setting up a SQL Server instance.
Creating a Database
In order to use a database, first we've got to create a database. Since I said we're going to use Azure for our SQL Server, I'm going to walk you through how to create an Azure database so that you can connect up to a SQL Server instance no matter where you're from. It just makes things a little bit easier that way. If you go to azure.microsoft.com, you'll get this start free screen, or you'll get something similar. If I can click on this, if you create a free account, you can get a $200 credit for 30 days, and that should be more than enough, like ridiculously more than enough for you to create a SQL Server instance out here just to play around a little bit so you can experiment with connecting to SQL Server from a Node application. All right. When you're done with that, run out to portal.azure.com, and that's where you're going to want to end up in order to create your SQL databases. What I've done is I've clicked on SQL databases, and that takes me to this screen. It's just a very simple, easy creation process. I want to create a SQL database. My database name is going to be PSLibrary for Pluralsight library. Now it has to be unique. If it's not unique, just create something else. Your subscription. I've got a Visual Studio Enterprise subscription. You'll have your free subscription that you created, or you'll have a subscription somehow, so just pick your subscription. We're going to create a new resource group called Library. We're going to create a blank database. Now we've got to set up a server. We're going to create a new server called PSLibrary. I'm going to create just a simple library, Library. Now it doesn't like that because it's not long enough. Look at this, and you'll see there is some restrictions on passwords. Uppercase letters, lowercase letters, numbers, not enough numeric characters. It's got to contain three of those. I'm going to just create a password. You can find out what it is later. But keep in mind, don't use mine 'cause this will go away. Create your own. Put in whatever logins and passwords you want, and put your location. I am not in Western Europe, I am in the Eastern United States, so that's what I'm going to go with. I'm going to select that. All right. Our pricing tier starts as standard. You can really pick whatever you want. You got basic, standard, premium. You can, in each, adjust what is required, what's not required, what you want. In basic, storage is very small, but that may work out best for you. It's cheap, but if you're on the free tier, you can do what you want with it, and then we're going to apply this, and go with that. I'm going to create. All right. Now my database is being created. It's going. It'll show up here in just a minute. Now I can start connecting to it from my Node database. If you wait long enough, you eventually get this, deployment succeeded. We're going to click go to resource. Here I have my database. Notice we've got nothing in it. We've got SQL Server. Nothing there. In order to do anything, we need a table. We're going to come in here. We're going to log in with the credentials that we had. Now notice I've got no tables. I'm going to to create a new query. I'll slide this over a little bit. I've got a query going here. We're just going to create a table in our query. We're going to create a table called Books. Let's see. We need title. Varchar 255. And we need author. Same thing, varchar 255. What that's going to give us is just the two columns in this table. We're going to execute this query by clicking run. Query succeeded. If I click this refresh right here, now I have a Books table with title and author. Now that only gets us so far. We've got to insert some data into this table. Let's do that, and then we'll be ready to go. I'll slide this back over. Here I've got my insert. I just went ahead and did it. You can copy it or you can do your own or whatever. Just get to this spot. Now you'll notice, we left one thing off of our query. I did it on purpose 'cause I want to show you one other thing, we have ID, title, and author. When we created our table, we only created it with title and author. We're going to show you how to alter a table in SQL Server just in case you want to add genre and read later as you're working through this. So just alter table books. We're going to add ID. That's an int. We're going to run this query, refresh this. I see Books has all three. All right. I'm going to go back to query one. I'm going to run this. Query succeeded, eight rows. If I do one more new query, I'm going to say select star from Books. I want to run this. Notice there is all my stuff with my ID at the end. All right. We have SQL Server set up. It's running. We've got data in there. Let's go start querying stuff out of our database.
Connecting to SQL
Now that we've got a database created, let's go through the process of getting our Node Express application connecting up to our SQL Server Azure instance. We're going to use a tool called mssql. If you go to npmjs.com and just look for mssql, it's just this one right here. Now this is going to do all of the work for SQL Server for us. It's going to open our connections. It's going to handle our queries. It's going to do all that stuff for us. All we have to do is install it and use it. The first step is going to be npm install mssql. Let's just run that. When it's done running, if we come into package.json, you'll see 4.1.0. That's the version we're going to use. As always, the first thing you're going to do once we have it installed is to use it. Let's do const sql equals require mssql. We'll save that. Now we're going to skip the quick example because I don't want to get there quite yet. So we're going to come all the way down to the bottom. We're going to look at this config document. I'm going to copy this. Right now here under where we set our port up. First, let's close a couple of things to make it better. I'm just going to paste that in there. I didn't paste anything special. I just pasted exactly what we had off the website. The user I created in my Azure instance was library. The password was pslibrary with a one 'cause I had to have something in here. Now for my server. This part is where you might forget, or you don't remember where you put it. If you go back over to portal, and you go to your SQL database, PSLibrary in this case, you'll notice right here, it has your server name. You can actually just click it, and it's going to copy it to your clipboard for you. Now we're going to come in here and just paste that. And then the database we created, or it's just PSLibrary, just like that. I'm going to leave this encrypt section exactly the way it is, and then we're going to close it out. Now I'm going to use that config to open up my connection. At this point, we just go sql.Connect, and pass in the config. Now this returns a promise that lets you do stuff after your connection is done. I love autocomplete except when it doesn't do what I want it to do. Now it returns a promise, but I don't want to do anything with this just yet. I'm going to do just a .catch right here on the end just in case there is a problem. If there is a problem, we'll have that lined up. It doesn't like curly braces for one line, which is fine. Really, I don't need the parentheses either. We'll just clean that up. There you go. All right. Now I get a connection. Let's save that, open this up. Type npm start, hit Enter. There we go. Now everything just broke. Interesting. I got an error that's spit out from my connection. Cannot open server requested by the login. Client is not allowed to access the server. That's 'cause we didn't do one thing. Let's go fix that problem. It's important. I kind of wanted to demonstrate this to you instead of just doing it upfront so you get an idea of how this works. Now my connection was open, but it couldn't get there because it didn't get to the IP address. We're going to come back over to our portal. Right up here, we have a set server firewall. I'm going to click on that. They've made this really easy. I just click this add client ID button, and it's done. I save it. It updates the rules. Now I'm done. I close this. I do an npm start. That'll work. Okay. I've got a connection, but I'm not doing anything with it yet. Let's pop over to bookRoutes, and let's start using this thing. Let's minimize books because we're going to delete that here in just a second. Same thing here. Const sql equals require mssql. We're going to save that. Let's come down inside our BookRouter. Inside our get, let's actually try this out. All we have to do is create a request and then we're going to use it. So we're creating a new sql.Request. Red squiggly just means we haven't used it yet. And then we just go request.query, select star from books. Now we've got a bunch of options here. We can use callbacks, we can use promises, we can do async, we can do all of those things. I'm going to start with a promise. This is going to return a promise with a .then on it. It's going to send me back. If we come back over to our documentation, it's going to send us back a result, and then we can catch the error. Right here. Let's look at what the result is. Just so we're not confusing things, let's put our book list up inside here. Okay. There is my open. What we're going to do now is let's look at the result. I'm not ready to just drop it in here into books 'cause I don't know what this looks like. We want to debug result. The problem with debug first of all is we haven't created it. Let's come up here, const debug equals require debug. Remember, we have to pass something into debug. What do we want to pass? Because we could pass app but we're not in it now, so I don't want to just pass the same thing we passed before. Ideally what we want to do right now is I'm going to pass app.bookRoute. We'll save that. Now that's not going to work because we're not using bookRoutes. I'm going to come back over. When I say using, I'll show you what I mean to package.json. To our start, we have debug equals app. I don't want just debug equals app, I want all of it. We're going to save that. We're actually going to close everything and start it over again. All right. This is all red because we don't have parentheses around result. Let's add that to clean that up. I don't need it, but if I'm building something bigger, it likes to have it. Down at the bottom, we'll stick our semicolon, clean up our empty lines. We'll save that. Watch it restart down at the bottom. If we click on books, we get back our list, but remember, that's just the hard-coded list. Over here, look at this, we've got bookRoutes. Make this big. We've got recordsets with a whole bunch of objects. And then we have a recordset with all of our stuff in it, title, author, ID. That's what we wanted. And how many rows, rows affected. Result.recordset is what we want. Back in here, let's do books equals result.recordset. That should be what we need. Refresh my page. Now it works, I guess. If I hover over more, notice it's still books sub zero, but War and Peace is actually one. The reason why it's doing that, if we come back over to our views, our bookListView, here we've got nav.title. Scroll down to where we have our, there is the books of i's of author. Read more is i. Really, what I want is that to be .id. We'll save that. Refresh. Hover over read more. Now you see we've got one.
All right. It's one thing to query everything out of a database, but let's talk about how we query specific things and add some query parameters to what we're doing. Basically what we want to do is we want to create this page, just book sub one, and query a single book. If you recall in the last clip, we made this work. But now in read more, I just want to click on book one. The way we're going to do that, if you look down here, bookRoute, we pull an ID out of req parameters. We're going to try and do the same thing that we did up here down there. We're going to start by pulling an async function. The way we do that, remember, is we're going to create an IIFE, which basically is two parentheses. The way I always say this is two parentheses and then put a face inside of it, so it's two eyeballs and a nose. It's kind of gimmicky, but a lot of times, if things don't work, people have forgotten the second eyeball, and all you can say that in training, it's like you've left an eyeball off. Okay. At the beginning, async function. We're going to give it a name just because yes, it wants us to. And then we're going to move all of this up inside. Format, clean it all up. All right. Now this sql.Request, let's just copy down. Okay. I want to talk about two things real quick. The first one being, we pull in a single request, and we pull in the query, but notice, we never connected to the database. I want to talk about that for just one second. Over here in app.js, we connect to the database. This actually does the connection. Remember, we hit the error. Before we queried anything, we just hit the error right here on connect with our firewall issue in the last clip. If we come over to bookRoutes, and we look up at the top, how is it that this is already connected? The way that works is when we say require mssql here in bookRoutes and over at app.js up at the top, it's the same instance of mssql. The way Node works is it creates an instance of mssql here, and then it just passes that same thing. It caches it and passes it everywhere that it's being used. That's very helpful when here I can do a connect, and then I just pass the same thing around everywhere that we go. When it comes over to bookRoutes, and I do sql equals require mssql, it's the same thing for map, so it's already connected to a database. That just makes our lives pretty easy. All right. If we come down here, I have const request equals new sql.Request. Now I want to query. Old school, I'd go like this. I'll move this down here where we do something like this. This is gross, and we don't like this. It actually even says that is preferred template, and we'll get to that later. But this is gross, and we don't want to do this. What we're actually going to do is something like this. The way we do this. .input. You'll even notice when I hit a dot right here, it actually is listed right here in my options, request.input. What input is going to take is a name of itself, ID in this case. That's what we're passing in. We can pass. Here you go. Just with the help, you've got the name and the value or you can do the name, the type, and the value. We're going to do the name, the type, and the value. It's sql.Int is what we're passing in. And then we're going to pass in the actual ID. And then we're going to chain to that our query. We'll actually pull this right up here. Select star from books where id equals @id. And then that will get us result. What result is going to be. Let's actually just look at that. We'll go to the next line, and we'll do debug.result. Got to refresh. And then you'll see the result that came back was recordset again. Notice it's still an array. So we are going to take recordset. Result.recordset sub zero. Now we've done this twice now. Look at this, result.recordset thing. We don't like that. I really just want to say hey, we're going to pass back the recordset. Remember destructuring. Let's just do this and pull it out. I want result.recordset. We'll take it out of book. There you go. We'll get rid of our debug, save that. Let's see if this all works. We have refreshed. Let's go to our book list here. We got our list of books. We'll click on number three. And we get the Time Machine. That's connecting up to SQL. That's the basic idea here. We have a query, we do a config, and then our connect. It just works for us. The idea is not to do a lot. If you go back to npm, you can read through some more of the documentation here. If you continue with SQL Server, and do inserts, so here's input. You can do transactions, prepared statements, all of those types of things to get this all going. I've gotten you marching you down a path of SQL Server. I at least wanted to expose that to you a little bit. Now there's one thing left though that I want to touch on, a phrase that you may have heard called middleware. I think we even used that a little bit in this conversation. I want to go into bookRoutes, and I want to introduce you to this concept of middleware around our slash colon ID.
I want to take just a minute to introduce you to this concept of middleware, and show you some things that we can do with it. Now one example of middleware that we already have is if we go back to our app.js, we have this app.use morgan tiny. Morgan is a piece of middleware. What morgan does is as something comes in, it looks at the request, and logs something out of the console. When we get to passport, passport is middleware. Basically all middleware is, is a function that is executed on everything that comes in. Here, let me do a debug. My middleware. Let me save this. Now if you're familiar with middleware at all, you already know there's going to be a problem here. That's fine. Let's restart our app, flip over, and let's just go back to books three. Notice I'm just sitting and spinning. Everything's broken, but I come back over here, and nothing's done at all anywhere. If I call next and save. I'm going to restart. Now I get it although I don't get my debug. The reason for that, if we come back over to package.json, we did app. There we go. I got my app here. We did colon star, but with we forgot to leave app on. All right. Let's come back over here. We'll save that, refresh our page. Now you see we get my middleware. Everything that's called. Notice, boostrap.min, my middleware, everything, everything that you asked for gets this little my middleware thing. If we take next back off, and save that, what next does is say hey, when you're done with this, run the next thing. Now I can refresh, and notice now I'm going to sit and spin, but I did get my my middleware. What this is used for ultimately is I can interrupt the process flow, and I can do whatever I want to do. We'll use this from time to time throughout our application. I'm going to show you one place where we want to use this right now. If we come over to our bookRoutes, on line 72, we have bookRouter.route with an ID. What this is, is I'm going to go pull an ID. We've got our .get. But ultimately eventually, we may also have a .posts, a .put, a .patch, all of those things. What I don't want to have to do is replicate this book selection code every time. It doesn't make sense for us to do that. What we're going to do instead is we're going to implement a piece of middleware. We're going to do that. In a route, you use .all. Instead of route.use, we're just going to do a .all. What .all is going to take is a function, request.next just like everything else. We're actually going to take this entire thing, cut it, leaving the .get empty, and pasting it in to the all. Now we're almost there. Basically what's going to happen now is no matter what you do, going to slash ID, whatever you do, put, post, get, we're going to return back this bookView, and we don't want to do that. We're just going to take this back out. Just take the render, put it back down here. All right. What we've got is in all, we are going to go query for the ID, and then do nothing with it. What we want to do is we want to go query the ID, and then pass it along to everything else. The way we're going to do that is we are going to modify our request object. We're just going to add something to it because all the request is, is an object that's passed around from one piece of middleware into the next. We can manipulate it. We can do things with it. We can say req.book equals recordset, and then call next just like that. We'll save that. Now what this is complaining about right here if I hover over this real quick, it says to use array destructuring. We talked about object destructuring. We're doing it up here on line 75. What array destructuring is, is when I just do that. Now req.book becomes whatever recordset sub zero was. If there was more than one thing, you could just add commas. Array sub one would be the next thing. This is fine. I find this to start becoming confusing. You can do what you want. I'm going to leave this like this. What I would potentially do in a personal project is I would leave it like that, and I would change the rule, but whatever works. Okay, now that this is here, we now have to use it. Here we have next. Down here, we can say instead of returning recordset, we could just return req.book. There you go. You can harden this up. You can check to see if there is a req.book, then you do something. If there isn't a req.book, you return a 400 error. There's all kinds of stuff you could do. But for right now, we're going to leave it like this just so we can make sure that this works. I want to save this. We're going to come back over. Everything still works. All right. That's it for middleware. That gets us where we want to go. Now let's switch gears, and move from what we have here with SQL Server, and we're going to go and connect to a MongoDB database instead.
Now that we've looked at one side of the extreme with SQL Server, we're going to now look at the other side with MongoDB, and the comparison between the table-based structure and the unstructured setup of MongoDB. MongoDB tends to be fairly popular on the Node.js side. If you're coming from a .NET or Java background, I'm going to spend a little bit more time on the Mongo side because this is something you may not be as familiar with. The first thing we're going to do is we're going to go right on to mongodb.com, and we're actually going to install Mongo. I know on SQL, we just set up a cloud instance. The reason why we use the cloud for SQL Server is because you can't install that on everything. I'm working on a Mac, or if you're working on Linux, I don't have an option to install SQL Server on my Mac, so we set it up on the cloud that way. MongoDB can install anywhere. We're just going to go through the motions of installing Mongo locally, so we can play around a little bit more. The first thing we're going to do is we're going to click on solutions, and we want to try it now, MongoDB 3.6. We're going to click try it now, and go to the community server. This is what we're going to download. Notice for me, it defaults into OSX. If you're on Windows, that's cool. Click on Windows. And then we're just going to download it. Now that'll start downloading. I'm going to click the back button though 'cause there's one thing I want you to look at. If you go back to community server, there's an installation instructions right here. On Windows, it's in the same spot. If you click that, it actually has fairly detailed instructions on how to install. If you're on Windows, I'm not going to walk through the Windows installation in part because it's actually fairly straightforward 'cause you get an installation file. You just double-click the MSI, and it kind of goes. MacOS is a little bit different, so I'm going to walk through that one, but click on the installation instructions, and you can kind of see what's going on. Now this is downloaded. I'm going to open a terminal window, and I just created a directory called MongoDB, and I'm going to drop this TAR file in here. I'm going to right-click, open it with the archive utility. Now I have a file. I'm going to copy all this, paste it, and then delete what I just downloaded. Now I have just a bin directory and some readme stuff. That's cool. If we open the bin directory, you'll notice a couple of things. We've got two executables here that matter to us. We've got mongo and mongod. Mongod is the actual server. We're going to run that to start up a server. Mongo is our client application. That's what we're going to run to connect to our client. Let's go to a terminal window. I'm already in the bin directory here. I'm just going to type ./mongod. That's going to fail. The reason why it's going to fail, if you work through the installation instructions is it says we don't have a /data/db directory. We're going to work on that by going make dir -p /data/db. Now I'm getting a permission denied. You may or may not, but if you do, just come back and sudo it. Now I don't want mongo to have to be running in admin mode. I don't want to sudo mongo, so we're actually change the permissions of this directory so that it doesn't have to do that. We're going to do a change mod 777 /data/db except we can't do that because I've got to sudo it. All right. Now I should be able to just type ./mongod, and now my database is running. We can check that by opening a new terminal window, and running mongo. You can see a whole bunch of stuff here, but ultimately we are connected. I can just do this. Show dbs, and see I've got two. I've got admin and local. You see over here on the server side, I have this received client from. That means hey, I'm connected. I've got MongoDB up and running. It's ready to go. Now let's actually start playing around with this a little bit. Let's create something on our server that's going to install data for us. In the SQL Server side, we wrote a install script. On the Mongo side, let's go a little bit more in depth, and let's actually create an adminRoute in our application that's going to populate data for us.
Alright, what we're going to do next is actually build a series of routes in our application that will handle the insertion of data into Mongo for us. I'm going to do this for a couple of reasons. One is we only have one router set up right now, so I want to give you an opportunity to create another router. This also is just an easy way to take the date we've already typed once, and just copy and paste it, and then have the application inserted for us. That's what we're going to do. If we pop back over into our application, I've reverted back to how we had it set up back at the beginning of this module. I've taken all of that SQL Server stuff out of it. If you need to restart, just pull down the course materials, and the starter module six code should be in there. You can just start from there if you would prefer to do that. The first thing we're going to do is we are going to come in and create a new route called adminRoutes, and this is the routes that's going to deal with this insertion of data for us. Let's click on routes. Right-click and create a new file, adminRoutes.js. If you look at bookRoutes, here at the top, we've got express, require express, and bookRouter equals express bookRouter. Let's just snag those two, and paste them here. Now instead of bookRouter, we're going to do adminRouter. All right. Now to create a route, remember we have this setup as a function. And then down at the bottom, we export this function. Now you could, and I haven't talked about this, you could just do this like that, and that's perfectly fine. I prefer to have my exports down at the bottom all in one place. That's why I do that. But yes, if you asked the question, why can't I just do that, you can, that's fine. All right. Now we're going to set up our route. adminRouter.route is going to be that. And then we're going to append to that our .get. What we basically want to do with .get is we want to have it insert a whole bunch of stuff for us. Before I go any further, look, I'm getting all these red under squigglies. That's because I created a new file down here at the bottom. See, I have spaces four. We're doing spaces two. I'm going to format my document. There you go. It all cleared up, not all of it but most of it. We'll just get that out of the way real quick. Now I've got my .get. What we're doing with our .get is we're going to use that to insert. Basically I'm just going to go to localhost/admin, and it's going to drop a whole bunch of books in there for me. That's the idea of what we want to do. Our get is going to take in arrow function, req and res. This arrow function is going to insert the books. Now we're not quite ready to do that. I don't want to drop on my Mongo code in here yet. Let's just do a res.send, inserting books. I add my semicolon. Hit Enter right here to clean that up. We're not using nav. I'm just going to leave that there because we may use it later. Actually we're not here anyway but I'm just going to leave it. This one, we haven't ever done. Go ahead and clean that one up too. All right. We've got that there. Now we just have to hook it up. We've got our adminRoutes created. We just have to hook it up in app. Let's come over to app. Right here where we do bookRouter equals require bookRoutes, I'm just going to duplicate that line, and then I'm going to say admin. Here we have app.use books. Here we're going to say app.use admin and adminRouter. Before we only had one. Now we've got two. App.use is basically saying hey, if you get a request to /books, send that stuff to the bookRouter. If you have a request for /admin, send that stuff to the adminRouter. Those were pulled up right up here on 24, 25. That should be all set. Now if we just come down and do an npm start, we've mistyped something, so come back over to our adminRoutes, and look at what we forgot. Down here at the bottom. Our function router. Let's look at this error. I want to give you an example of some of the weird errors you're going to get. You got a type error. Router.use, which is essentially what we did right here, app.use, requires a middleware function. Remember, a middleware function is something that has req and res and those types of things to it. We got a get type function instead. It's something weird that didn't even work. What we forgot is we have our adminRouter here. When we execute this function, it doesn't do anything. We've got to actually add right down here, return adminRouter. We forgot to return our adminRouter. Now when app.js executes this function right here, it's going to get back an adminRouter instead of nothing because that's what it was getting back before because we weren't returning it from adminRoutes. All right Now we're working. If we go to /admin, we now get this message, inserting books. That's where we want to be. If you got that, you caught up. Now let's look at MongoDB, and get our things going.
All right, now let's spend some time writing the code that's going to insert some books. Let's actually start using MongoDB in our application. Just like we did with SQL Server, we're going to go out to npmjs.com for our MongoDB side. We're just going to type in MongoDB. You see here, the official MongoDB driver. Let's click on that. You can usually tell the success of a package by looking down here, downloads last month. This one is used, so let's use this one. You can get documentation right here. It goes out to MongoDB's documentation. Lots of stuff out there. But for us, I'm going to walk you through it, so you don't need to worry about any of that. But MongoDB is what we're going to use. If we come back, we're going to close this, and we're going to do an npm install mongodb. If we go our package.json, there it is right there, 3.0.1 is the version that we are using. Now that we have MongoDB installed, we need to start using it I'm going to do this just a little bit differently than we did on the SQL side. On the SQL side, we went into app.js, and I opened a connection over there. I'm not going to do that for this. We're going to do it a couple of different ways in the various things we're doing, but for right now, I'm just going to do it all right here in adminRoutes. Up here at the top, we're going to do mongoClient equals require mongodb. Now in order to use it, what we actually have to use is not, we're not pulling in all of MongoDB, we're actually just going to use MongoClient. Now remember, in the fancy new world, we don't have to do this anymore. I wanted to type it out so you at least saw it and understood, but now we take this, and we destructure that. All right. Now that we have a MongoClient, we come down into our actual router. I'm going to minimize some stuff so that you could see what's going on. We're going to do all of our database stuff right in here. The first thing we're going to do is create a variable just for our URL, mongodb://localhost:27017. That is the standard port that everything runs on. If I come back over to my terminal that mongod is running on, I'll widen this a little bit so you can see it, when it was done setting up, it says waiting for connections on port 27017. That's just the default port. That's what we're going to do. Then we have to have our database name. In this case, think of it just like SQL Server. SQL Server has databases, Mongo has databases. We are going to call this libraryApp. The big difference between Mongo and SQL though is I have not done any work on the Mongo side to create that database. I'm just calling it libraryApp right here. I just typed it in. I haven't typed it anywhere else. And it's going to create it magically for me when it's not there. That may be great or it may be concerning to you. Either way, it is what it is, but that's what we're doing. All right. Now just like the SQL side, we're going to use async await as we're dealing with Mongo. You can do it with promises. If you go back out to the MongoDB documentation, and look here, it walks you through using its promises. That's cool. We need to get comfortable using the new stuff. I'm going to do everything in async await because it lines out very nicely. We can just go from there. We're going to create an IFFE. This is my standard how you create an IFFE. We're going to create a async function. We're going to just call our function mongo. I do this, remember, just because if I don't, then it yells at me because it doesn't like unnamed functions. Naming your functions is a general good idea. Inside our async function, we're actually going to create a thing called a client. This is going to be what we're going to use. We're going to create it up here because now check this out, I'm actually going to do a try-catch block. I want access to the client but in both, and I want to be able to close down below. I'm puling my client up here. My try, we're going to open our client. We do MongoClient.connect. Did I type everything correct? We're connecting to our URL, which is localhost:27017. Now it's going to wait. It's going to wait for that to happen, which is exactly what we want. Actually let's just spit out so we can see it. Hey, we connected to the server. Now I don't have debug yet, so let's add that. In this case, we want it to be app.adminRoutes. Now that we've connected to the server. The next thing we do is actually pull the database. We're actually going to tell our client, hey, we're going to use the database libraryApp. Once that's done, now I'm connected, I've got a database, I'm going to insert some data. The way we do that is by awaiting db.collection. Now the collection is kind of like, some people would be highly offended by this statement, but it's kind of like a table. It's not a table at all. But in SQL Server, you have databases and tables. In Mongo, you have databases and collections. It's kind of like a table, but it's not at all like a table. You'll see what I mean by that here as we go into this. But we're going to create a collection just like we created a table called books. And then we are going to insert many books. Books. Now I didn't copy books over. Let's come back over to bookRoutes. There's books right here. We're going to copy that. Just remember, I've minimized it, but it's just this list of books. We're going to come back up here to the top, and we're going to paste it. Now I have this thing called books. That's our list of books. Okay. I'm going to insert many. What this will do is it's going to return the results of that. Let's actually save that into this variable called response so we can look and see what that looks like. Let's send that back. We haven't talked about JSON yet. We've done a res.send. We've done a res.send file. We've done a res.render. Res.json is going to send back a JSON object. It's going to format it for us. It's going to set the header types all properly. It's going to do all that for us, but it's going to send back a JSON object, which just so happens to be what our response is. That makes it pretty simple just to send that back as JSON. Now in case there's a problem, let's log that out. And then down when we're done, let's close our connection, and then get rid of our inserting books. That's it. We've got it all set up. Now let's go try it and see what happens, and then go from there, and I'll show you a couple other things about Mongo as we go.
All right, now that we've got our code written, let's test it to see what's going on, and we're going to talk a little bit about Mongo as we go. If I pull my thing back up, and I do an npm start, and we go to localhost:4000, everything should still work. I'm going to now do /admin. Now look at what we get back here. This is interesting. We got a result that says eight. That's how many books we inserted. And then we have this ops, which has all of our stuff in it. Now notice a couple of things. I'm going to go back to our terminal window for a second, and I'm going to do show dbs. Now I have a libraryApp database. I did not have that before. Like I said in the last clip, it's just creating it. If you actually look at our thread over here on mongodb or the mongod, you actually see this line right here, createCollection: libraryApp.books. It just creates it because it needed it, which is great. That's a fundamental distinction between SQL Server or Oracle or MySQL and MongoDB, which is more of the unstructured. It's just going to create whatever it needs. All right. The other thing it enforces, it created this underscore ID. Everything now has an ID. When we do an insert, and part of why we pull it back is we want access to this ID so we can kind of start doing stuff with it. All right. That's all there. It's all good. I've got data in the database. If we go back to our command line, I can actually do a use libraryApp. And then I can do a db.books.find().pretty(), and it will give me my books. You see, they're all right here in our database. It's all there. If you don't do the .pretty, you can do it without it. It just looks gross. .pretty makes it all nice and it looks good. All right. Now that that's there, let's start implementing mongo over on the bookRoutes, so we can start pulling stuff out and do some searches over there.
All right. Let's implement select many, or pull out a list of books from our MongoDB database to make our books page work. One thing I want to fix real quick as part of our adminRoutes 'cause we're going to use this in our bookRoutes as well is notice I have, on line 66, a debug statement that didn't get printed out. The reason why is 'cause when I reverted everything back to the beginning of four, we didn't add something back in. Let's come back over to our package.json file. Here on line eight where only DEBUG equals app, let's add our app colon star. Now we'll get our messages spit out that we needed. Okay, let's come to our bookRoutes. We're going to hang out in this file for a little while. First thing we're going to do is we're going to come up here, and we're going to add our two lines, MongoClient and our debug. Instead of adminRoutes in this case, we're going to do bookRoutes. Okay. We've got require mongodb. Our debug equals our bookRoutes. If we come down into our slash right here, if I come back over to adminRoutes, I'm going to borrow much of what we did. I'm going to do our URL, our database name all the way down to our try. I'm going to copy all the way down to db.collection books, not the insert many, just the collection. We're going to come back over here, and we're just going to drop this in right there. After our render, we're going to close our try, we're going to close our client, and then we're going to close everything else. All right. We're going to format that. All right. We're all set. Now I just have to implement everything. Here we copy this over. Let's change this. We're going to just say our collection. Our collection equals await db.collection. Now we have to do our find. The way that works in Mongo is instead of a select select like we do in SQL, we actually use the word find in Mongo. It's going to return back our books, our collection.find().toArray() because it's going to hand back a whole big thing, and I don't want all of that, I just want the array. What find does in this case, it's just going to hand us back everything. For us, that's cool. We don't need to filter yet. We're going to filter in the next clip. Right now we just need everything. Col.find, just give me everything. And the these books is going to come down here, and overwrite, and be done. Let's save that. We're restarting. That's it. It should work. Let's open up /books and see what we got. I've got books coming back. It looks like it worked. If I come back over here, you'll notice we still have our thing. The reason for that is in our package.json, we changed this, and that's great, that's correct, but what we didn't do is restart, which you actually do have to do. All right. Now let's refresh. now you'll see connected correctly to the server from our bookRoutes. Okay. The best way to know that this worked, and we're pulling from the right place is we're actually going to come into our bookListView, and look at one thing. Notice when I hover over read more, I still get zero and one. Now I actually have real IDs that I want to use. We're going to come back over to our bookListView. Right here on line 105 where we say i, we're going to say books of i dot underscore ID. And then we'll refresh this. Now you can see I'm pulling those out. Now when I click on this, it doesn't work because everything's horribly broken at that point, but here at this page, it totally works. What we need to do now is implement this single route. We're going to do the same thing that we just did up here down below to give us our book.
All right. We've got this select many out of the way, which was the easy one because it's just give me everything you've got in the database. We've got that part covered. Now we're going to do a select one, which means I've got to start doing some queries, and I actually have to provide a query into my Mongo find. Let's talk a little bit about how to do that. If we look back, let's talk a little bit about what we're doing, we're here on books, and I want to be able to click on this read more, and go to this route, and actually have it work. It doesn't work right now. Blows up because this thing doesn't exist in that array that we have. Let's fix that. All right. If we come back into bookRoutes, almost everything we do in this clip is going to be in bookRoutes. If we come back down, the first thing we're going to do finally is we're going to delete this array. I have had it minimized most of the time, but it just clutters everything up, so let's get rid of it. We don't need it anymore because now everything is going to be in the database. We're going to come down into our bookRouter. Let's copy these two things. We're going to type everything else, but let's copy those two lines of code. Right here under our ID, remember we pulled in our ID right here, under that, let's paste these two things. And then we're going to do our async function again. I always type an async function like this, and I do that on purpose because I want to make sure I've got the whole thing lined up. Then I'll come back in, and I'll fill it in. We're going to do an async, async function, and we're going to name it just because. You can name it whatever you want to name it. It doesn't really matter, but in general, we want to have a name to function there. Okay. Now if we come back up here, a lot of this is the same. We're going to let client, we're going to try client, we connect it to the server, we connect it to the database, we connect it to the collection. All that's the same. Let's actually just copy that. We're going to drop it right there. Let's close that and catch. And then if there is an error, we're going to debug our error set. Now comes the interesting part because we've got our collection, and remember up here, we did a books equals col.find().toArray(). What we're going to do down here is const book equals. Same basic idea, col.findOne. What findOne does is it just finds the first one. If I do a find and do at a query that limits to just one, I still get an array back. FindOne is just going to hand me back the one individual thing, and I don't have to worry about arrays. Now when we're finding in Mongo, I'm actually going to pull the terminal up here for just a second. Db.books.find, the way we find is we add in an array. I'll make this bigger for you for a minute. You pass into Mongo a JSON object, title, just like that. Now we only get the one item. This is how it works. We're going to pass in a JSON object to filter down in just the one item. Now in this case, we want underscore ID, and we want our ID. Now in theory, this is great. In practice, this doesn't work. Now the reason why it doesn't work is if we come back over here and notice that our underscore ID is not just a string, it's this thing called an ObjectID. That's different than just the string. That's important for us. What we actually need to do is use Mongo to build out this ObjectID. If we come back up here, right now we've got MongoClient. We also need ObjectID. ObjectID is going to do this thing for us. Let's copy that. We're going to come down here, and we're going to pass into our ID. Instead of just ID, we're going to pass in a new ObjectID, and we're going to save that. Now once that's done, we're going to take our render, and we're going to render it, but instead of books, we're just going to do book. And I don't even have to do that, I can just do that. Okay, one thing we forgot, we got to add await in front of our findOne, so it's going to wait for that to happen, and then we're going to render it. Let's spit out right here the book just so that we can look at it a little bit, and make sure we're doing the right thing. All right. Let's save this. Let's clean it up a little bit. All right. We have restarted. If we come back over here, let's hit Enter, and now you see I get my book. If we come back over, you see right here our connected to server, and then our book just comes back as an object. We've got our ID, our title, genre, authors. It's just the object, which is great, and that helps us get our stuff done the way we want it. All right. That's it for Mongo for right now. We're going to add stuff later, and we'll implement some more things when we start doing our login and user routes. But right now we're able to pull data out. Let's add some security in the concept of a user so that we can sign in and we can sign out and we can restrict access to things using passport in our next module.
All right. We got quite a bit done in this module. We talked about a couple of different concepts while we were at it. We're working our way through building a web application. Now we actually have something that we could call an application where we're displaying some data that we're pulling from a database, we insert data, we did all of those types of things. We started by getting data from SQL Server. We set something up out on the cloud. We did our basic selects from SQL Server, both the single select and the select multiple, and we got that all working. I then showed you the other extreme with MongoDB, and how now we've got a Mongo database running that we can use to insert and pull data from. We looked at a little bit of the Mongo command line just so we can play around with that a little bit. And then we inserted data using some admin routes. I showed you how to dump some data in, and then how to pull out both one and many items. Now that we've got all that working, let's add the concept of users, and start dealing with this thing called passport that's going to handle our authentication for us.
In this module we're going to talk authentication and creating users for our web application. Now, we've come quite a long way in this course on building a web app with Node.js and Express. And we've got databases set up, we've got routing done, we're pulling stuff out of the database, displaying it to the screen, all that's there, but there's an important thing that we're missing that most applications that you might be working on require, and that's the concept of a user. So in this module, we're going to start creating users. We're going to create new login, the password, you can sign up and sign in, and all those types of things. We're going to do authentication. You'll enter a username and password and we'll validate that that's correct, and we'll log you in appropriately. We'll do authorization. And so, authorization is the ability to block certain pages. We're actually going to block most of the website if you're not signed in. So, you can't get to the profile page. You can't get to the books. We'll block everything if you're not signed in. And we're going to do all this using this thing called Passport. And Passport is the default npm package that Express uses for user management. We're going to mostly use the local strategy in this course, but there's other strategies for handling Google, and Facebook, and LinkedIn, Integration, and all those other things. So, let's get some stuff done.
In this module, we're going to create a sign up form. We don't have this concept of a user at all yet, so sign in doesn't work. We've got to start with sign up. And so, we're going to change our first page up in order to create this sign up form. So, if you're following along, your home screen should look like this. We've got this Library home page with the standard items that came with the template. Our books page actually has some books in it, but our home page has what was left over from when we created the template. We're going to repurpose this to build our sign up form. And so, if we go over to our view, and we open up source, views, index.ejs, this is that home page. I'm going to minimize everything so you can see more of the screen. If we go down to line 96, depending on where you're at it might be a little different. It's going to take this right here from this anchor tag all the way down to these two breaks, and I'm just going to delete them. And we're going to use this space right here to create our form. And so, let's start with this. Let's just say, "Hey, sign up," and then we'll create a form. In our form we're going to post but we don't have anyplace to post it to. And what we're going to do with this, right now we've got, if we come back over here, in our routes we've got our adminRoutes and our bookRoutes. And either one of those is what we want to use. We're going to use a new set of routes called auth. So, auth/signup, that's what we're going to post to when this form gets filled out. And then we're going to have two things. We're going to have a username and a password, and they're both going to be inputs. Now, I'm going to leave the type of password as just text, mostly just so you can see the passwords that are being typed. Normally for a password we'd have type of password like that, but we're not going to do that because I want you to see what's being typed so you can validate it later. All right, the last thing we need is a button. So, if we did this right and we come back over here and I refresh my page, now we have a sign up form. And you'll see mine, I use a form entry thing that automatically fill my password in because I do a lot with local host, but that for you is most likely blank, but then we've got all this stuff. So, let's delete that. We're going to delete pretty much everything from this line right here all the way down to this a href. We'll leave these two divs. So, about 186 is where I'm at right now. We'll save that and refresh. There you go, we've got our website up. Let's go build out the rest of this. So, we've got now to build this auth/signUp, which means we've got to build a route, we've got to build some views potentially, we've got to hook all that stuff up. So, let's go do that in the next clip.
Now, that we've got a sign up form, we actually need to implement the routes that are going to handle the post of the form to create that new user. So, we're going to go out to our source directory, our routes, and we're going to right click and create a new file called authRoutes. Now our authRoutes are going to require a couple of things, the same thing as normal here. So, we need express. We will eventually need MongoClient. And let's go ahead and pull in debug. And for debug, let's say app:authRoutes. Now, we've got all kinds of red underlines because we're not using any of this stuff. So, let's go ahead and start using it. Let's do function, our router function, and then module.exports router. What we want to do in our function is create our authRouter and then create our routes, just like we've done everything else. And we've been doing this up here. It doesn't matter, you can do it down below if you want to. We're just doing const authRouter equals express.Router. And then we do authRouter, and we're creating the sign up. Remember over here in our index.ejs, we posted to sign up. In our authRoutes, we're going to sign up. Now so far, we've only done gets. We've always just put here a .get. Well, in this case, now we're going to handle post. And while I'm thinking about it, let's type sign up correctly. There we go. So, this is what's going to be used whenever our form posts to /signUp. But ultimately, it works exactly the same way. We still have a function with req and res. We still pull things out of the requests. We post things to the response, all of that stuff. But we now have one thing we need to do. Because if we look at index, we have this form with our username and our password that's going to be posted to our authRoutes. And this works a little bit different. Remember over in our bookRoutes if we scroll down, we had our :id, and we pulled out our req dot parameters, and all of that stuff, that just worked. The body of the post works a little bit differently. And so, we need to pull in a package at this point called body partial. So, we're going to pull this up and at this point I'm going to show you one more thing. I've got my app running here. And normally, we've been Control + C, cancel it, npm install, I'm going to stop doing that. We're far enough along you should be comfortable kind of with this part of it. I'm going to click this plus right here. And this is going to add a new terminal window. Now, I have two. I've got node and bash. If you're on Windows, you'll probably have PowerShell here. That's the option that shows up in Windows 10. So, if I'm here I can just do npm install body-parser. And if I go to my package.json file, you'll see there it is, 1.18.2. And so, what body-parser's going to do is look at the body, and then pull everything out and put it in our req.body. So, we need to tell it to do that. So, heres our app.use statements. We're doin' morgan, we're doin' express static, all of those things. Let's add our body-parser to this. So, up here at the top, we need to pull it in. And then down here right after app.use morgan, we're going to use body-parser. Now the order does matter, so we want to keep it up here at the top. We don't want to have it down at the bottom after our books and our adminRoutes are used because it's not going to work quite right. So, here were going to put it up here at the top, app.use. And we're actually going to go ahead and do two things, because most of the time you're going to want both of them. We're going to do bodyParser.json and execute that, and then app.use bodyParser.urlencoded. And then urlencoded requires an object with extended: false. We're just going to take that for right now as some boiler plate code. That's setting up body-parser. Don't worry too much about it, just this is what gets us there. Now, what that's going to do is pull out our post and add it to our body. And we can try that and we can play around with it by coming over to authRoutes right here and doing a debug req.body. Put our equals there, format our document. And then don't forget, right down here at the bottom to return authRouter. Change our indent using spaces to two, because that's what I've been doin'. The semicolons are a fine line, right? So, if I have one, it says, "Hey, you don't put one." If I don't have one, I got an error that says, "Hey, I need one." So, semicolons are a little weird. All right, we're going to save it, 'cause in either case it'll work just fine. We're going to save that, now we got to wire it all up. So, we come down here to our adminRouter. We're going to do an authRouter. So, pull the authRouter in, app.use authRouter, get it all set up and we should be good. I'm going to break something for a second. I'm going to do this and I'm going to save that. I'm going to flip this back over to where we're running, and I'm going to refresh this page. And you'll notice like everything just stops, which is painful and interesting. I'm going to add that back. And now, I'm going to refresh my page and it works. So notice, I didn't even get like a helpful error there. There we go, there's my helpful error. I'm going to save that. The reason for this is because it's using this bodyParser.json as a middleware. And so, it calls this function bodyParser.json, and it's waiting for bodyParser.json to call next. And it's not calling next. So, it just sits here and spins. So, if you're having something like that happen, double check and make sure that your middleware is all there and correct. Now, that that's there, everything should be wired up. Let's refresh this page, and let's type jon and password. We'll click sign up. Now, it froze, it didn't do anything, but look at this. In my authRoutes, here my debug req.body look, body pulled out username and password. And so now, we're froze, we're spinning because we're not passing anything back. And so, we can fix that. But I wanted to kind of to show you this, username, password, all of it set up. Now here, I can do res.json and send back my, I'll just send back my req.body. And res.json just sends back properly formatted json. It sets your headers properly. It does all that stuff for you and it sends back json. So now, when this refreshes, and I click sign up, whoa, now everything wants me to save. I get jon and password. All right, that's that. Now, let's implement our Passport to actually create users and add users to sessions and things like that. Let's get into the meat of this module and use Passport.
All right, we're going to use Passport in this course to deal with user authentication and authorization and all of that. And if we run out to npmjs.com and you can actually just type passport, you'll get this right here. This is the default thing that most Express applications are using. You'll see it's got over a million downloads last month. It's just the easy, simple way to deal with authentication and authorization. There's documentation here you can work through, but I'm just going to do it. So, let's jump in and start playing around with this. So, if we come back over to app.js and minimize everything, we are going to use Passport. And so, we need to pull Passport in. But what Passport does, well let's kind of work through it. So, const passport equals require passport. Now, what Passport does is deal with maintaining your user object in the session. And it also deals with dropping it in a cookie, and pulling it out of a cookie, and applying it to sessions, and all those types of things. So, that all being said, we need a couple more packages. We need cookie-parser and express-session. Now, cookie-parser is just kind of like body-parser, it does the same thing and it's also with a dash, and express-session. So, let's Control + Tilde, pull this back up, drop over to our other one, and install these. And if we jump to our package.json, see cookie-parser, express-session, and passport. All right, we've got those set up and we're goin'. Now, we have to use them. So, we're going to scroll down, same way we did here. We've got bodyParser. Now, let's use cookieParser. And then cookieParser's example, don't forget to execute cookieParser. And then app.use session. Now, session takes something called a secret. It's just something it uses when it builds the cookie. And so, what are we going to do? It really doesn't matter, so I'm just going to do library. Format that, all right. Now, we need to do the Passport stuff. And there's going to be quite a bit of it actually. If we come back over to our documentation, setting up Passport, serializeUser, we'll talk about what all this stuff means. There can be quite a lot of code here, and I don't want to just stick it right here in our code. I want to pull that out somewhere else just kind of like we did with the routes. So, what we're going to do is just do a require, source, config, password.js right here, and pass in app. And then we'll just move all of that Passport code that we would have done right here over into this other thing. Now, we got a red underscore because that thing doesn't exist yet, but we'll go create that and put everything in there. So, notice we've created this new thing called config, and we'll put other stuff in here, too. So, we'll create a folder called config. And this is where we'll put all of this type of thing. It'll create a new file called passport.js. Now, this is just a file, right? I mean, we don't need to pull Express. Password doesn't care about Express because we're passing the app in. So, all we really need right here is passport. That's the only thing that we're going to care about. Now here, I'm just going to do module.exports. And I'm going to do this just so you can see both ways to do it. We've been creating a function and then exporting it. Here I'm just going to do module.exports so that you can see this is an equally viable way to do it. Just kind of figure out your own personal preference. And we're going to export this function called passport.config. Change my spacing. Now, Passport does a few different things. Now, remember I said Passport, the first thing that it's good for is maintaining your user object in the session. And there's two functions it uses to do that, serializerUser and deserializeUser. And what serializeUser does is store the user in the session. And what deserializeUser is to retrieve the user from the session. And the way it does that is by executing a function. And so, we'll fill these in as we go. The other thing in order to get ready for that, we've got to do two things. So, we've got to initialize. This is where Passport just sets itself up on the app, on the request as it comes through. And then we have app.use passport.session. And that's going to just build up the session and pull things out. That's actually where we're going to start calling the serialized and deserialized users. Now, what these two things take are functions, just like everything else. And it's going to pass in two things. It's going to pass in the user and a done callback. And then we have to then take that and store it into the session. We have to do what we want to do with it and then store it in the session by calling done. Now, we haven't really talked about callbacks very much in this course, mostly because everything we're doing is a single wait. But in node we have a way that we do things in callbacks, it's error first and then the thing, whatever it is that we're doing. And so, you always check the first one to see if there is a problem, and then you do something with the second one. In this case, we're just going to pass in null and the user. Now in this case for us, we just have a user and a password. We're not going any further than that. But oftentimes, we don't want to store the entire user in the session. We only want to store a piece of the user. And so, you could actually do user.id and only pass the id of the user in. And then down in deserialize, you could pull that id back out. We're not going to worry about that right now. We'll talk a little bit more about that as I'm building our deserialize user, but just for right now, we're going to store the whole user in the session. And then deserialize is the same idea. Actually we can just copy, check this out, the whole thing. Now down here, if I was doing .id, that would be userId. We can call it whatever we want to, right? It's our function. But what Passport would be passing back in was just the id. And then we would... do something with Mongo where we'd go find the user and then we'd pass that user into done. But I'm going to set it up just like this and be done. Now, all that we're doing so far is we're taking the user and we're storing them in a session or pulling them out of a session. That's not really what we want to do totally. There's a whole lot more to this. And the problem with authentication in general is there's so many different ways to do it. So, we're doing username and password, but you could do log in with Google, log in with Facebook, log in with all these other things. And I don't want to have to write a whole bunch of different code to do all these different things. So, what Passport does for us is it implements this idea of a strategy. And if you scroll down right here, you use strategies to do it. And actually, if we come up and we just look for passport strategy, and we look at popularity, we'll just sort by popularity. Passport-local, local usernames and passwords. That sounds like what we're doin', so we're going to use that one. Just scroll down to, they've got an oauth2, they've got a Facebook, they've got a Google, they've got a Twitter, there's LinkedIn, there's just everything. And that's what you can use to use that type of authentication. We're just using local, so we're going to use passport-local. But again, there's going to be a whole lot of code. And I potentially am going to do it a whole lot of different ways. We're not in this course, but as you build it out, you might have a Facebook, and a Google, and a Twitter, and all of those. So, I'm going to break these out. And I'm just going to require right here a strategies directory and a local strategy. And that's where we're going to put all of our Passport code around local strategy. So, we'll save that, we'll save our app. And then I'll put a stopping point right here, and then we'll go implement this local strategy and get our stuff going.
Now, that we have the pieces set up for Passport, let's implement this local strategy and see what that looks like. So, the first thing we need to do if we come back over to our terminal window is actually install the local strategy. So, we'll do an npm install passport-local. And if we go to our package.json file, you'll see there it is, version one. Now, in Passport we said, "Hey, we're going to pull "from strategies, local strategy." And you'll notice, I'm just going to call this out right here, eslint is giving us a hard time because there's this unexpected require just kind of sitting in the middle. And you'll notice over here in app.js, we did the same thing and it doesn't have a red squiggly underline. And the reason for that is because we're passing app in here, which implies that we need to get to a certain point and then pass it in. Over in Passport, we don't have to do that. So, I'm actually going to move that up to the top so we can get rid of that squiggly line, and it doesn't matter. And that's kind of the cool thing and we'll talk about that just a little bit here in just a minute. Now, we have to create this thing. So, let's right click on config and create a new folder called strategies. And then we'll create a new file called local strategy.js. Don't forget to put that js, that's probably the most important part. What we're going to do now is actually build out what the local strategy should be. And what that looks like, first of all, we've got to pull in Passport. Const passport equals require passport, and then we're going to pull in a strategy. So, passport-local is a object. It's a package that has this thing called the strategy attached to it. And in the olden world, you'd see pulling in .Strategy. We're not going to do that in our new world because we're going to destructure it over here, just like that. Okay, same thing as passport, what local strategy's going to return is a function. I'm going to give it a name, localStrategy. It doesn't matter what you call it because you're just exporting the function. But it's generally a good practice to have that function have a name. Set our spaces to two. Now, all that this file's going to do is tell Passport about this local strategy. So, the only thing it needs to do is say, "Hey, passport.use," and we're going to use a new strategy just like that. I like the red squiggly lines 'cause it reminds me, hey, strategy's not defined or we're not using it, that means I spelled it wrong. You probably yelled at me as I was typing, hey, you spelled it wrong, but I couldn't hear you because I recorded this a little while ago. So, passport.use new strategy. And what the new strategy thing takes is two things. And now, each strategy's going to be a little different, but localStrategy looks like this. And basically, what the strategy is is how we deal with a username and password, and how we identify that as a user. And so for us, the first thing we're going to pass in is the usernameField. We just have to tell it, hey, strategy, our usernameField is called, and come back over to index.ejs, username, and our password field is called password. And what that's going to do for us is now the strategy's going to deal with pulling that out of the body. And that's just a little shortcut. That's all it does. And then the strategy is going to call a function, and it's going to pass that function, the username, the password, and the done callback. And so now, it's up to us to define what that user is. And so, this is where here in a couple of clips, we're going to go out to the database, and we're going to validate the user, and we're going to check their password, and we're going to do all that code right here in this thing. But for right now, let's just create a user. We're just going to make one up with the username and password. And then we're going to say, "Hey, that's our user." We're going to call done, with null, and our user. So now, if you log in with a username and a password, it's just going to create a user with that username and password. That's all it's going to do, nothin' fancy. Wrap those in tick marks because these are strings. Don't forget. Make sure we're all done. Bring that up a little bit, all right. Now, our strategy is done. But we're not really doing much with it yet. So, let's go ahead and implement just a little bit so we can play with this. So, let's go back over to our authRoutes. And we're going to right click, I'm going to close everything else, just mess with our authRoutes. When you post a sign up, right now we're just taking what's in req.body and showin' it. What we should be doing is creating our user and then doing something with it. And we're not quite there yet. In the next clip we're going to implement creating the user and saving it to the database. But really, what we're going to want to do is log that user in. We're going to create the user right here. Once the user is created, we're going to log them in. You know how when you create a new account somewhere, you don't then have to go log in, it logs you in automatically. And so, that's what we're going to do. And we do that just with req.login. I now have this thing called login on my requests. And where that comes from, to go back over to passport. When I do passport.initialize, it creates these things on the request that I now have access to and login is one of them. And so, req.login, we're just going to send it req.body, which is the username and password. And then it's going to log that user in. And then once it's done logging that user in, we want it to go somewhere. And in this case, we're going to don our redirects. We've done res.render, res,json, all these things. Res.redirect just tells, it sends a redirect to the browser and say, "Hey, go to this other place instead." So, that's what we're going to do. We're going to actually go to auth/profile, and that doesn't exist yet. We're going to create that here in just a second. But what auth/profile's going to do is just show us the users created. So, what's going to happen here is when you sign up, I'm just going to create a login, which before we get to all the Mongo stuff, I want to show you what this looks like. And then I'm going to go to auth/profile and that login exists. So then down here, we're going to create another route called profile. And we're going to attach a .get to that. And what we're going to do with this is we'll take a req and res, just like normal, but what we expect at this point is because I'm logged in, Passport is going to take that user, it's going to serialize it into the cookie, it's going to do all this stuff, all magic for us. But when the request is made to the profile, it's going to attach that user as if by magic to the request. And so, let's just send it out. So, let's just look at what happens here. We'll just get rid of that for right now. We'll come back and deal with it later. Here, let's save that. Now, if we've done everything properly, this is still running, close that. Let's go to auth/signUp. Here we pass Jon a password. Now, let's say, "Hey, jon, and I'll use Pluralsight "as my password," and we'll click sign up. Ah, it says, "Cannot get auth/profile." Why? Because I left my slash off. I don't think we've talked about this, but that slash is important because it defines where you're going. Sign up, there we go. This is our user. So, our user was created, and a whole lot of stuff just happens to flow through all of that. Because here we took our req.body, jon, and Pluralsight. We logged in, and then we redirected. Which means then Passport shoved this user into the session. When this auth/profile get request came it, it pulled that user back out of the session, and then created that user, this user that we have right here, it added it to the request and it moved on. So, if you're here, the plumbing works. And I wanted to make sure your plumbing worked before we got to where we're dealing with Mongo. So now, if you've got this piece, everything's working. Now, let's actually go through the motions of getting a user created.
Creating a User
All right, now that we have this post working, let's actually go ahead and implement the Mongo piece to get this user created. Now so far, we've got this post working where we have this username and password. And we're just going to create the user with those two pieces of information. There's no real reason to build out this big user object at this point, because it's going to be different for everybody. So, I'm just going to keep the username and password, and we're going to create our user around that. And so, when you post, I'm going to minimize everything again, when you do this post right here on line nine, what we want to do is add this user, create this user in the database, and then call this req.login. So, all the work we're going to do is right here on line 10. Well, it's where we're going to start anyway. There's going to be a whole lot. So, right here we've got req.body, and if you remember, req.body includes username and password. And so, we need to pull those two things out. So, the first thing we're going to do is pull username and password out of req.body. Then we'll pull in our Mongo URL, because we're going to need that. And we'll pull in the database name. Same things we've been doin' all along. And then we're going to create our async function. Always create it like this. Just a helpful tip because nine times out of 10, no, not nine times out of 10, but often, you'll forget this other trailing pair of parentheses. I always, always, always create it like this and then fill it in. It just makes my life easier. I'm going to call this function addUser, because that's what we're doin'. Now, what we want to do, first things first, is create our client. And then we're going to do our trycatch. And then we're just going to do all this stuff we normally do. So, we've got to open our connection. And then let's go ahead and throw our debug out that just says, "Hey, you connected to the server." I like to have that just because it shows, hey, things are happening. Then we're going to connect to our database, library app. And then we have to connect to our collection. Now, up till now we've been using this books collection. We don't want to put this in the books collection. So, let's put it in a new collection called users. And this is just another one of those cool little things about Mongo, or terrifying, depending on who you are, that says, "Look, I can create a new collection just by using it. "I don't need to do any extra work." So, let's create a user with the username and password. And before I go nuts, I'm going to add my space right here so that all those red lines go away. It is a little funny that eslint to get ahold of you just throws all of this stuff at you and then goes away when I just do that. So, I've got my collection open. I've got my user created. Now, we need to do an insert. And we haven't done an insert yet. All we've done so far is reads. We've pulled things out. Well no, we did do an insert, remember? We did an insert in our adminRoutes. Here we did an insert of many, and then we just dumped the response back. Well, we don't want to insert many. We want to insert one, but we still want the results. So notice, I've got insert, insertMany, and insertOne. I'm going to insertOne, we're going to insert our user. Now, what that's going to send us back is a big object. Let's actually look at it. And then we'll add something here just in case. There we go. Now, let's save it, let's just stop right here. Now, it's going to die because we're not doing anything, but let's try it and see what we get. We'll pull this back up. Okay, so now, when I click sign up, let's pull this terminal window back over here, we used this a little while ago. If I do a db.users.find I get nothing. If I click sign up, it's going to give me my Pluralsight. And the reason why it did that instead of just spinning is because I went ahead and called my req.login down here. So, this happens, it went ahead and worked. It didn't wait. So, just async, only asyncs to this point. Req.login was still down there, so it still worked. But here's what I got back from the database. I got kind of this big thing. If you click the little caret, it's huge, it's this big, long thing that said, "Hey, you got a CommandResult, "your connection information." And then right down here, you have this thing called ops, O-P-S, that is you're username, password, and then that id that we want. When I come back to my terminal and I do db.users.find, there's the user I just inserted. So, what I actually want is this ops and then the first item in this array. So, if I pull this back down, what I'm going to actually use in my req.login, let's delete that. I'm going to come up here to my debug results and there I'm going to req.login. And we're going to do results.ops sub zero. And this is now going to be the user that is created. Now, I'm going to save that and it's going to restart. Now, one important thing that I'm going to call out right here is that every time the server restarts, this goes away. And the reason for that is this auth profile is stored in a cookie, in a session. And every time my session restarts, or every time my server restarts, this cookie goes away. So, if I go to slash profile right now, it's empty because my server restarted. Let's create a different one, I'll click sign up. And now, what you'll see in my profile is I've got my id. That means this user was inserted into the database. We're kind of there. That's some pretty good progress actually. We have our user being created. Now, what we want to do is use our local strategy to pull up out of the database and authenticate our user.
Now that we have sign in there and posting, let's actually set up our validation so we can log a user in. Now, all of this work is going to be done in localStrategy. So, I'm actually just going to close everything else. Now, what we want to do when a user logs in, right here, when I'm pass this username and password, I'm going to go into Mongo and I'm going to figure out everything from there. So, a lot of this you've seen before, so I'm going to kind of go quickly through it. Now in this case, when we're debugging local.strategy. So, I just dropped some code in here that we've typed a ton of times, URL, our database name, our iffy with our async function. We're creating our client. We're opening our connection, db.name, which is libraryApp, our users collection, all that. Now is the point where it actually starts to be different. So, kind of type all this stuff. I'll scroll so you can see it all. But it's the same code we've typed a whole bunch of times. But what we're going to do now is let's actually go find the users. So, this is sign in, so let's find the user associated with the login that we just had posted to us. So, let's say user await and we're going to findOne. And in this case, what we want is something with username. And remember, we can shortcut, it would look like that, but we're shortcutting it to just look like that. Okay, once we have that user back from the database, and because we did a findOne, we don't have to do the ops sub zero and all that. It just hands us back the first one. So, we say, "Hey, if the password "of the user we just got back matches the password "that got passed into our function up here," remember down below the way this worked, we passed no for error and user for the user. So, up here if it works, we're going to say, "Done, "no for the error, and user for the user." If it fails, we're going to pass still null, 'cause guess what, it didn't error, it just failed. And we're going to pass false for the user. Clean it up just a little bit. There we go. So, if the passwords match, it's going to pass. If they fail, it's going to fail. And what that means is if I come back over to my authRoutes, if it's successful, we're goin' to profile. If it's fails, we're goin' to slash. All right, so let's save that and let's see what happens. So, jon password, right down here, jon Pluralsight, or Jonathan pwd are my options. So, jon password isn't an option. So, if I click sign in, I get kicked back 'cause it failed. If I type jon Pluralsight and I click sign in, I'm getting taken to my profile and there's my id. So, that's how that works. The localStrategy says, "Hey, I'm just going to, "you're going to hand me username and password, "I'm going to pull it out of the database. "I'm going to deal with it. "I'm going to validate it. "And then I'm just going to drop a user on the request, "so then I can just say req.user at the end." So, that gets us to where now we have a user, but how do I protect my routes. So, I don't want you to go to profile if you're not logged in. How do I do that? So, in this next clip, we'll finish this module off with how to protect your routes.
Now, let's talk about how to protect our routes so that you can't get certain places unless you are allowed to. So, let's start with our authRoutes. You should be able to get to sign in if you are not logged in. Either of those routes is fine. Profile, on the other hand, I don't want you to be able to get to it unless you're signed in. Because profile will be empty because I'm just sending back the user object. And the way we do that in most cases is with some middleware. So, I'm just going to say, "Hey, execute this function "every time somebody tries to do anything "to /profile, req, res, next." And here, think about it just for a second. If a user is not signed in, Passport won't put a user object on the session. So, if there's a req.user, I'm good. If there isn't, I'm not. So, if req.user, call next, otherwise, res.redirect. And now, my profile is protected. If I save this, it's going to restart my server so I am automatically not logged in. So, if I hit profile, it'll redirect me back here. If I do log in, though, I can go here and I can get there. So, that's working. What about books, though? So, if I go to book, I can see all the books. If I click read more, I get the one book. What I want to do is protect all of books. You can't get to books at all. Both the id and the slash total if a user doesn't exist. And the way we do that is we just attach something right on the bookRouter. We just do a bookRouter.use. The same way we were doing app.use, we can do a bookRouter.use, and just append our middleware here. And then we do the same thing, what we just did in authRoutes with this, we're going to do right here and save that. Now, because I saved it, it kicks me out. It logged me out and I hit book. We go to books, I can't get there. I do jon Pluralsight, it logs me in. I go to books, now I can get there. So, the way you authenticate everything is just with doing checking for the user and checking to see if they're valid. Now, you can authorize even more by saying, "Hey, if req.user.admin, or .roles includes something." Because if you remember in our localStrategy, we're just pulling the user out of the database. So, if you append roles or whatever to that user, it just works. One thing I'll challenge you guys to do on your own is institute log out. I was restarting the server in order to log out. I will tell you if you do /logout right here, same place, same where we do up here, we just do req.login. There's a req.logout that will sign you out. So, if you institute a logout button, you just call req.logout and that deals with that for you. All right, that's it for Passport. Now, let's go and finish this course out with a talk about third party APIs.
All right, we made quite a bit of progress in this module. And now, we're kind of starting to look like a real application. We've built out this web application, this library application, that's going to hold some books from a database, display them on a page. In this module specifically, we instituted Passport, which handles all of our user authentication. We created some users, then we did authentication, where we logged in and we used the local strategy to validate the user and then create that user object in our session. We also did authorization just by checking to see if the request had a user on it. And if it did, great, if it didn't, then we kicked you back out to the home page. This next module, we're going to wrap up by talking about how to make third party API calls. And we'll use a package to help deal with our HTTP requests.
Structure and Third-party APIs
All right, we're going to round out this course with this last module, and we're talking about a couple different things. We're going to talk about the structure of our code, and we're doing that on purpose, because we're going to build on some services for some third party API calls. Alright so, we are almost done with this course of building a web application and if you've made it this far, which you have, because you're listening to this then great job, you've learned a lot and we're going to round this out by talking about some code structure. Right now, everything's in one file. Like all of our routes have all of our code in them, and that's not very testable. It's not very easy to deal with, and so we're going to talk about how to pull those things apart, and implement some controllers, and our controller is what's going to have all of our router code built into that. We're also going to build some services, or specifically, a third party API service. So, we're going to hook up to Goodreads, which is a website that deals with books, and we're going to pull information from a Goodreads API to populate our web page.
Alright, we're going to start off by talking about controllers and implementing some structure to our application. So we're going to go to our book routes, and I'm going to minimize everything. And if you look at, like our bookRoute. Look at all of this stuff. So if I were to ask you, just by looking at our routes, what routes does bookRoutes handle? Like, what does it do? It's actually more difficult to figure out than you might think. So we've got a dot get on slash, we've got a dot get on colon ID, that's it, but over on our authRoutes, we've got a dot get and a dot post here, and it actually isn't as simple as just looking and seeing what's going on. So what I want to do is try and simplify this just a little bit. Now we're going to institute this concept of a controller. So let's come over here first. Let's come right here and say hey, we're going to have this thing called a controller... Coming from controllers/bookController. And we're going to move all of this code into this bookController. So let's come over here. Let's go up to our source directory, create a new folder called controllers. Rename that to be controllers, and then create a new file called bookController dot js. Now what bookController's going to do is be a function. And then we're going to export it. So what, but bookController's going to do is just be a series of functions. It's going to have a function called getIndex, and a function called getById. And these functions that the controller has are going to substitute for bookRoute and the colon I'd here. And we're just going to replace this with that. So basically all we have to do is we take this function, everything in this dot get right here, and cut it, and drop that right there. So basically all I did is I took getIndex, fix our spacing, is take getIndex, from inside this get, and just created a function over here. Alright now let's wire that up. And there's a few things we've got to do to wire that up. First and foremost, we need nav, right down here, so let's pass nav in, into our bookController when it's created. And so down here, right inside our router function where we have nav, we want getIndex. Let's execute this function. Execute the bookController function, pass in nav and then we want to pull getIndex back. So now we're passing nav into bookController right here, and what we're expecting back is an object with this getIndex in it. Right now, getIndex is just a function. I'm going to minimize that, and minimize getById and come down here and we're going to use that's called the revealing module pattern. And what the revealing module pattern is, is I've got a function with a whole bunch of functions in it and I'm actually going to return an object that has the two functions in it and the reason why we do this is when all our codes here, if you don't have code folding, 'cause not everybody does, you just scroll down to the bottom and say hey look I've got my two returns, or my two functions that this returns. This controller handles getIndex and getById for the bookRoutes. You'll see debug is red ungular squiggly 'cause we don't have that, and Mongo is red ungular squiggly 'cause we don't have that either. So let's add those two things from bookRoutes. We don't need ObjectID yet, but we will. And then we'll change our debug, instead of bookRoutes to bookController. Alright, now what that does, is now I have a function. So let's think about this for just a second. I have a function that takes a req and a res, and just looks at rec dot body, and actually this one doesn't even look at rec dot body, and just calls res dot render and passes some stuff into it. So this is actually very testable. I can just test bookController and pass in res and actually have mocha course out there that covers testing of all of this stuff. So once this is done, pop over there and watch that course to learn more about testing with these. Okay, we're going to save that, bookRoutes. Now, all we need is to use getIndex, just like that. Now the slash id, we pull that out, we just copy everything from there or cut everything from there, come over to our bookController, scroll down to getById, and paste. Get rid of our, we're not doing an arrow function anymore, so get rid of that. Other than that, that's it. Let's save it. We'll save this. We'll say hey, getById. We'll pull that out, too. Alright, now I'm going to save. I restarted. If that works, then everything's working, and actually you'll see our debug stuff will change. I'm going to change one more thing while we're at it. I'm going to comment all of this out. We're going to save that. The reason for that is, I want to be able now, at this point to go to this book, and have it work because if I left our middleware there, that was protecting the books, then it's not going to work. Now look. Now I have my debug stuff coming from bookController. So we move, we didn't write new code. I mean hardly at all. We wrote a few lines. For the most part, we just copied everything over into bookController for bookRoutes. And actually let's just pull this out, too. Let's just call it middleware. Now we're drop our middleware over here, and our bookController down here at the bottom. Let's create a new function called middleware... And return that. Save it... It'll restart. Now I got a book... Go to read more, and everything still works. But, now when I look at bookRoute, my router code, especially when I get rid of these two things that I don't use anymore, it fits on one page. I can say hey, slash does getIndex and slash colon id I just do a get on both of those. And that's all they do. So that's a controller and that's the idea of controllers in our application. It was a pretty quick little look, but I wanted to give you that idea, because now we're going to do the same, basic idea, but for services, so we can make a third party API call.
Now chances are, your application is not going to be standing alone. You're going to have to call other APIs or other services somewhere else, and so I'm going to walk you through an experience of how to deal with third party APIs. And we're going to use, in this course, the Goodreads API. And if you go to goodreads.com/api, you're going to get this page. And the first thing I want you to look at is, up here at the top, the developer key. If you click that, I have a developer key all set up. Now, don't copy this because I'm going to click this reset and it's not going to work and then you're going to be sad. But, if you click that, go and create your own developer key, so that you can start to play around with this API. You can play around with this page without a developer key and that's great, but for our stuff you're going to have to have one. What we're going to do for this, is try and fill in this read more on War and Peace. I want the details of this book down here and maybe the book cover as well. And so the API call we're going to use for that is show. So if you scrolls down books dot show and you click on that, this is going to hand us back all that information, and we click on sample url, and basically you get this XML response. War and Peace just so happens to be 656, and we want this right here. This detail here and then there's our image and we want to deal with this stuff, and so notice the call. Just goodread.com/book/show, the id of the book, and then the key is the developer key that we need to have. In this case it throws one in there just for show and for playing around, but you'll want to go create your own on top of that. So this is what we're going to do. We're going to make an API call. We're going to pull this data out of the book, and then we're going to display it on our webpage.
Alright, so the way we're going to deal with this API is using a service, and when I say service, what I mean is kind of like what we did with a controller. I'm going to come out here to our application and I'm going to close everything, except for our bookController just to fix our open editors, and I'm going to create in source a new folder called services. And under services, I'm going to create a new service, a new file called goodreadsService dot js. And we're going to put stuff in here. Now what we're going to put in here, let's talk about. Let's actually build all the other pieces first, and then we'll come back and we'll fill this in. So if I come back to my book controller, what I actually want to do. Let's go to bookRoutes first, actually. So if I go to my bookRoutes, where I create my bookController, let's actually pass in this goodreadsService. And it's dot dot slash, because we're moving up to services slash goodreadsService. Now the reason why I called a bookService instead of goodreadsService is because I may change that. I may use a different API at some point, and I only want to have to change it in one place and here I'll have to change my require, but if I just use bookService from here on out, I won't need to worry about it. Okay, so then in my bookController, I'm going to pass in my bookService. So we'll save that. Now let's go to my bookController, and hey, now we're getting a bookService here. And what I really want to be able to do, in my controller, is down here, I want to say, in my getById controller, after I do everything from Mongo, I want to come down here and add some details from bookService dot getById. So I want to create a thing that has a getById on it, and I'm going to pass it the id and it's going to hand me back some details. And then I'm going to use those details later on. So let's implement that, and the important part is I'm awaiting. I'm not using callbacks, I'm not promises, I'm not doing, I'm using a wait, because I want it to fit nicely in with our Mongo db stuff. And so let's talk about how to implement that over in our goodreadsService. And it actually really is fairly simple at this point, because all a goodreadsService is is a function. It doesn't even take anything. And it has one method. And I'm actually going to call it getBookById. So let's come back over to our bookRoute or our bookController, and change it to that. And then we're going to return that function. Now what this function has to do, is return some details in a way that a wait's going to work with it. And node's pretty cool in the way this is done because really, it converts promises to something that uses a wait. So if I just return a new Promise here, that's going to work. Just return new Promise. I now can use a wait. So with a Promise we have a function that receives two parameters: resolve and reject. And in this case we're just going to resolve. We're going to add a description, our description. Now this function needs a name. Let's call it goodreadsService, and then we'll export it. Okay, now we don't need anything passed in. This goodreadsService just works. We're not passing anything into it. And so instead of, over in bookRoutes, where it pulls in the bookService, I was looking in controller. It's not in Controller, it's in bookRoutes, where I'm requiring it, everything we've done up til now I've had to execute. I'm not going to do that this time. I'm going to go ahead and execute it here. And so I'm going to just return this object with getBooksById. It's going to work just fine. It'll be great. So I'm going to save this. There's one piece left. Now, if we look at our controller, I'm going to pull the book out of the database and then I'm going to add this book dot details. And on the details, I'm returning a description. So let's copy that and let's go to our bookView dot ejs. I'll minimize this. I'll scroll down to right here where we have our book dot author, our book dot title. Inside these Brs right here, let's go book dot details dot description, and we'll save that. Now if I refresh this page, I now get our description coming from our service. So bookRoutes, or the bookController added this thing and added to book dot details that did all that work for us. So the book goodreadsService just created this object for us with our description that now flows through. And despite the fact that I'm returning a promise, notice in bookController, I didn't have to do a dot then on the end. I used a wait. It was great. Now, let's actually like wire up to the Goodreads API and pull something real back.
Making API Calls
Alright now let's actually go make that API call and pull that data back. Alright, so in order to do this, we need a couple of things. The first one being, we're getting XML back. So show/656 XML. We're going to get XML back. I don't want to deal with XML, I want to deal with JSON so we're going to use one tool for that and if you watched the previous version of this course, we used HTTP. I don't want to deal with HTTP either. We're going to use something called Axios. And so let's go to our goodReads service. This is all we're going to work in so I'm going to close everything but the goodReads service, I'm going to minimize this. Let's pull up our window, and in here let's do an npm install. We're going to use something called Axios, and we're going to use XML to JS. And once those are installed, if I come back over here to my package dot JSON, notice xml2js is four dot 19 and Axios is 17 dot 1. Those are the versions that we're using. Okay, so let's come back over to our goodreadsService and let's pull these two things in. Okay, xml2js and Axios. And then let's go ahead and pull in debug, and in this case we'll use goodreadsService as our app, so just kind of whatever the file's called, that's what we stick in there. Alright, now what we want to do, and this becomes very easy, is inside our promise, we're actually just going to do an Axios dot get. Now, I could do the await stuff just like I have. I'm not going to here. I'm going to actually do a dot then and dot catch. We're just going to use that the way it should be. So for right now, let's actually just pull this right here. 656 it gets us back what we're lookin' for. Let's just do an Axios dot get for that. Once that comes back, we do a dot then. And if it fails we do a dot catch. Let's actually implement the catch first. Hey, if something goes wrong, we're going to reject and debug the error. If everything goes right, we're going to get a response. And what we're going to do with this response, is now deal with the XML that we get back, and that's where this xml2js comes in. So we're going to create up here at the top, a Parser. And Parser is just a function that we execute, it's going to create a new Parser. And we're going to pass in one thing: explicitArray equals false. And the reason for this, is because xml2js by default make everything an array. So if just one item is on the XML document it creates it an array and so it makes it just painful to deal with so you turn that off by setting a default. Okay so what we're going to do down here, let's look back at this document we get back. Notice we've got a goodreads response. We've got the request and then we've got a book and so all I want is the goodreads response dot book and that gives us the book title, the book image URL, and the book description. Those are the things we want. So if that works, we're going to do, we're going to parse that with our Parser. And it does this asynchronous thing, so in a true callback style. We're going to send the data over here, and then we're going to add a function. Now in true node style, we haven't done a lot of this so I'm going to kind of explain it again, we have error first and then the result. So you always check the first thing for if it's an error, otherwise you go with it. So, if error, we're going to debug the error. Otherwise, we are going to debug the result just in case something goes wrong, we can kind of see what we're doing. And then, instead of down here we resolve this, now, I'm going to resolve the result dot GoodreadsResponse dot book, and we'll save that. Okay so we did quite a bit here. We did a get to the 656, so we're hard coding that for right now. I haven't pulled in an id yet, although we will eventually. So let's go ahead and stick that there. And then we're parsing our response, and we're resolving our promise with that. So that means when we come back over to our bookController, we're taking whatever gets back and we're dumping it in book dot details. So let's look at that and see if that worked. I come back over to library and I refresh. I now get the full description in this big text. I also have this image URL, this image URL right here. Let's see if we can't drop that right there. So let's go over to our bookView, and here we have our image. Minimize this so we can fit it all on the screen. Instead of this source right here, I now want it to be book dot details dot image_url. Let's save that, refresh, and see if it worked. There you go. Now I have my War and Peace. Now we can fix it. We've got a class of image circle. So there you go. One thing though. We haven't talked about this yet. Let's talk about it now. Notice the Brs and the is and all of this stuff everywhere. That's because EJS by default escapes everything and that may not be necessarily what we want, especially in this case. I'm getting HTML back and I want to show it. So let's fix that, and the way we fix that is instead of this equals, we just use a dash. And the dash will eliminate the escaping of these HTML characters, so if I refresh, now we get what we were looking for. Okay, so we're almost done. It's almost working. Let's add the id in here and then we'll be done.
Passing the ID
So right now we have it hard coded. If I come back over to books and I go to Les Miserables, hey look, we're still getting War and Peace. That's because we have hard coded the id in our goodreadsService to be 656 and the reason for that is because we don't have the id in our database. So if we go to adminRoutes, and we look at our book lists, we need our id in here, and so I'm actually going to add a book id. War and Peace with 656, and we'll add one more. Let's do Les Mis as well. And you can take my word for it, you can go out and look, but, the book id for Les Mis is 24280. And we'll just save those two. You can fill in the rest if you would like to, but let's just save these two for right now. Now I've got 'em here, if we come back to our terminal. Let's drop our database. There's a little bit of a safety here, because it says right here, remove needs a query. Oh yeah, so you don't accidentally remove everything from your database, and actually it's a good thing it did because I don't want users, I want books. Oh good. Okay, so let's give it a query. So I said there was a little bit of a safety. It's not a lot of a safety, because I just dropped everything in books. Alright, so now that I'm here, let's add 'em all back. Now I've got book ids for War and Peace and Les Mis. Now we can double check. Yep, bookId, bookId. Alright, so now what I want to do, is in my bookController, here I was passing in book dot bookId, from getBookById. It's already there. I'm already passing it in, so let's go to my goodreadsService and use this Id. Now there's one last feature in VS6 that we're going to use that we haven't talked about yet. A string templates. So see the difference between the regular single tick and the tick mark that's kind up above the tab key. It's next the, on the same key as the tilde. Now notice everything turns this orange color. That's because the string template wraps everything and keeps your formatting and so the reason why we like the string template, is because it used to be I would have to do tick, plus, id, tick, plus, tick, and I don't want to have to do that. Instead what I want to do, is that: a dollar sign and then curly braces on either side. Now I can pass in id and that's going to work. So let's save that, come back over here to books, and see okay, let's click on Les Mis, I got Les Mis, and I got the Les Mis. And then let's click on this. I got War and Peace and I got my War and Peace description. That's it, we've covered everything. You should have all the pieces now to try and get this working. Let's sum everything up real quick.
Alright, that wraps up the whole course, actually. We're done, and just to summarize this module, kind of what we did is we've built out a web application. We've got all the pieces working, everything's there, we've got users, we've got all the stuff. In this module specifically, we started talking about structure. We pulled controllers out. We pulled services out. We've got our code kind of starting to line up the way it should, by having everything be separate. If you want to now use those structures in order to test things, I've got a Mocha course that you can go look at to see how to test the things that we have. We built controllers and services that are now testable and they're stand alone pieces. We ended with third party APIs. We called the goodreads API to pull some extra pieces of data, the image URL and the description that we incorporated into our book. And that's kind of it. Hopefully you had a good time with this course. I had a good time doing it and I'd love it if you checked out a couple of my other courses as well.
Released23 Mar 2018