Node.js App From Scratch | Express, MongoDB & Google OAuth

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] what's going on guys so this is gonna be probably the longest video that I've ever done that we're gonna jam-pack this application build into one video it's about two and a half hours long I've already recorded it I'm friggin beat but we're gonna create this storybooks app which is uh it's a crud application but we're also adding Google OAuth to it using passport using sessions and cookies we're gonna use MongoDB we're gonna store our users our stories and also our sessions we're going to save our sessions in the database so there's a lot of little things we'll be doing throughout this application that should help you in your no js' development we're also using handlebars as our template engine I didn't want to go off on to like a front-end framework or anything I wanted to keep it all server-side so I'm just gonna go through and show it to you and if you took my older dev to deployment course my no js' course on udemy which I retired about a year ago this is probably gonna look familiar this is from that although I did completely redo it and update it so basically we can this is the landing page and we can't do anything without logging in if I try to go to you know slash stories or anything it's just going to bounce me back here so I need to log in and if you have multiple Google accounts you can choose one I'm going to choose my wife's dummy account here so once we log in it takes us to the dashboard and basically it'll save your Google data in the database just like your name and your image your Google image and stuff like that and then here will be a list of your stories and you can create a story right here if I click add story and it just has a title I'll just say some story we can make it either public or private if you don't want others to see it on the public page you can set it to private I'll just say this is my story and save and also we're using a WYSIWYG editor here this is a CK editor so we can save that you can see that shows up here and if I go to public stories you can see that it shows right here now whichever stories are mine you can see actually three out of the four the the account that I'm logged in with then there's an edit icon all right I can't edit this one because this is my other goal will account so if I click edit I can you know I can change it to private save and now if I go back to my public stories it's not here because it's private and if I want I can click read more on one of these and if I want to read all of the stories or see all the public stories from a user I can click here and it'll show me just that user I can also click on this and it'll show me just that user ok so if I want to see all Jen's stories I can click here alright so I mean it doesn't there's not a ton of functionality but there's actually quite a bit that goes into this in terms of you know authentication and stuff like that protecting routes and creating all the story routes and stuff like that we can log out and I mean that's pretty much it so it'll take you know the video the recording was about two and a half hours long i juiced I will warn you I start to get tired at the end because I did this all you know straight through but I think there's a lot of information in here that that can help you out but again it's not a it's not a polished tutorial like some of my my other ones so hope you enjoy it thanks for watching and let's get started ok so we're gonna get started on our storybooks application and I think the first thing that I want to do is just set up our database and get that out of the way so we're using MongoDB which is a no SQL database and you can certainly download it and install it on your local machine if you want however I'm going to be using MongoDB Atlas which is a cloud version of the database so the data isn't actually stored on our machine it's stored on an Atlas on a cloud server so you want to go to MongoDB comm if you don't have an account you can just click on try free sign up do all that stuff register your email and then go ahead and sign in so I'm just gonna sign in to one of my kind of dummy accounts now once you sign in you'll probably see a different screen than mine because I already have a cluster set up what you want to do is just create a new cluster and you'll basically you'll see a screen like this and you can choose AWS for your provider you can leave all the defaults if you want to rename the cluster here you can and then just click create and it might take three or four minutes to get set up and then you should see a screen like this now the next thing you want to do is create a database user so you want to go to database access and right here add new user so for username was this password so this is the username so Brad T 1 2 3 4 I'll use the same or the password ok and then read and write to any database we're gonna keep that and then just click add user and then the next thing we want to do is network access so you can actually add your IP address here so that only your machine can connect to this database directly now I'm just gonna say allow access from anywhere because this is a production app or anything like that I also don't want to show my IP address so I'm going to go ahead and just do that and confirm and then if we go back to clusters as long as it's all set up you should have access to these buttons if we go to collections you'll be able to see your data here although we don't have any at the moment if we go back and we click connect and you click connect to your application you'll get this connection string which is what we're going to use later on when we're ready to connect our application so right now I'm just gonna leave this tab open and we're gonna jump into vs code now from here what I'm gonna do first of course is NPM and NIT which will generate a package.json file of course you need nodejs installed if you don't have it just go to node.js org and download and install it so description will say app 2 who creates public and private stories and the entry point I'm gonna make app is usually how I do it is if I'm if I'm building like you know an app with the front end using reactor view or something I'll call this server j/s but if it's a complete back-end server side rendered application I'll use app jeaious it's just preference so it's the author you can put your own name here if you'd like and then MIT for the license okay so we have a package Jason now there's quite a few dependencies that we're gonna be using quite a few packages so I figure we can just install them now and that'll give you a good idea of what we'll be doing what we'll be working with so let's install our dependencies so first thing we want of course is expressed which is our web framework to create routes and stuff like that we also need Mongoose to work with our database create models and so on we're going to use something called connect which is going to allow us to store our sessions in our database so that when we reset the server we don't get logged out okay and then we want Express the session for sessions and cookies we want Express - handlebars which is what we're using as a template engine if you want to use pug or ejs or something like that you can or if even if you wanted to just output Jason and use reactor view or something you can do that as well but we're going with handlebars for this let's see we want dot E and V for our config so the dot E and V so we can put our environment variables in there we're gonna use something called meth method override so this will allow us to make for instance put and delete requests from our templates because by default you can only make get and post right you can't do putting and delete by default but this method override allows us to so also need moment which is used to format dates we're going to use Morgan for logging we're gonna use passport for authentication and since we're using Google for our login we need the passport - Google - Oh auth to zero package okay because we're using OAuth 2.0 and I think that's it so go ahead and run this and all those should get installed now I do have just a couple dev dependencies so we're gonna npm install - upper case D node Mon so node model just can continuously watch our server so we don't have to restart it every time we make a change and then cross e + V I'm gonna use this because I want to put inside our scripts our start script and dev script I want to have a global and a global variable or an environment variable for our node environment and it's different depending on if you're on Windows or Mac or Linux or whatever so we're gonna just use that so there's no you know confusion now yeah everything is installed so for scripts let's create our start script so this is basically what we will be using in production I may deploy this I may deploy it to something like Heroku I'm not sure yet but here we want to run first of all cross E and V so that we can set an environment variable here because I want to explicitly set the node environment here to production okay so you want to set that to production and then we'll just run node and then the file which is going to be called app J s and then what we'll do here is this will be the dev so this is what we'll be running most the time and we want this to be the development environment and we weren't over on node Mon instead of just node alright so we'll save that we can close this file up and we can create our app J s which is basically our entry point so let's start off with just creating a just a basic Express server so we want to require Express and let's also bring in Dottie and V which will have our config our variables Dottie and V and then to load the the config file we have to let's put a comment hands a load config and we need to call Dottie and V dot config and then we just pass in here an object with the path to the config file which we haven't created yet but it's gonna be in config slash config dot env that's gonna be our where we put all of our global variables all right and then we'll initialize our app whoops Const app will initialize that with Express and then we want to call app dot listen now for the port I actually want to put the port in this config file so let's create a folder called config and let's create a file called config dot E and V and here we'll put our port and I'm gonna run it on 3000 now we also want to put our URI here we might as well do that now so our URI if we go back to our database and we click connect connect to your application we can copy this and we can put that in there we just have to make sure we replace password and then by default all it used to be test so now they just do this this DB name I'm gonna call it storybooks and it'll create it automatically so let's save that so now we at least we have our database string in there but let's close that up now for the poor let's create a variable here and we want to set this to process dot E and V dot port so whenever we use process dot E and V we can use variables that are in that config now if that's not there I want to run it on 3000 let's put 5,000 just so we can make sure it's coming from here all right and then we can just put that poor in here and then let's just console.log so for console.log I'm gonna just I'm gonna put in some back tics and I want to show the environment and the port so let's just say server running on and we'll say server running in and then we can grab the process a and V dot no D and V so we'll be running in either either development or the production mode on port and then we'll put that poor variable all right so let's save that and we should be able to run this so let's say npm run dev and we get server running and development modem port 3000 so we know it's coming from here I'm just gonna change this to 3000 and then I want to stop it and run NPM start and make sure that it runs in production mode okay so it's running production mode port 3000 but we want to run it and development mode I just wanted to test that out ok so that takes care of that I think the next thing that I would like to do is just connect to our database just to just to get that whole rigmarole over with so what I'm gonna do is in config let's create a file called DB j s and if you took my node J s API course on udemy or a lot of my nodejs videos I basically do this the same way all the time so we're gonna bring in mongoose choir on blues and then i have a function here called connect DB and i'm gonna call this a sync because when you work with when you work with MongoDB I'm sorry Mongoose you're working with promises okay so like Mongoose doc connect returns a promise and I don't want to use dot then I don't like that syntax I want to use a sink away if you want to use dot then that's fine but I prefer a sync away so here I'm just gonna put a try/catch and we want to try to connect so let's create a connection variable here and we want to await Mongoose dot Connect because this returns a promise and we want to pass in the connection string which we have in process dots env dot underscore URI okay now there's some options as a second argument here there's some options that we want to put in to avoid any warnings in the console one of them's new use new UART URL parser we want to set that to true we want to set use unified topology to true and we want to set use find and modify to false so this is going to stop some warnings in the console so after we connect I just want to console.log and we'll put soaps we'll put some back ticks in here and I'm just gonna say MongoDB connected and I'm gonna put the host so if we use that con variable and then dot connection and then host so that should log that now if something goes wrong and we can't connect let's console error whatever the error is and let's just stop everything stop the process so process exit and we want to exit with failures so we want to put a 1 in here and then finally we just outside of here we just module exports connect DB so that we can use this we can run this in the App J's file ok so pretty simple simple connection let's go to app J s now and let's bring that connect DB function in so we'll say Const connect DB and we're gonna take that from the config slash DB file right config the DB yes so we need to call this I'm gonna go right here and I don't need a comment actually we'll just say connect DB it's pretty self-explanatory so now I'm gonna run and save this and down here in the console you'll see MongoDB connected and it shows my my host cluster and all that okay so now that we have that out of the way let's see what we want to do next let's set up Morgan for logging which is pretty simple so we're just gonna bring in Morgan I just want it so that when there's a request to a page or any kind of request at all that it just shows down in the console so I'm gonna go right under where we initialized our app and I only want to run this in development mode so we'll just check process dot E and V dot node E and V if that's equal to development then we're just going to app dot we're just going to add the Morgan middleware which is just app dot use Morgan and there's different arguments you can pass in for different levels of logging I'm gonna use dev ok so that just makes it so it shows the HTTP method the response or whatever it just shows that stuff in the console so the next thing I want to do is set up our template engine so I'm gonna be using handlebars and first thing we'll do is bring it in and then there's just some middleware that we have to add so let's go up here and say Const exp HBS so Express handlebars let's require Express - handlebars and then the middleware I forget exactly what it is so let's search for Express handlebars all right so let's see there's actually we can make it so that we don't have to use the dot handlebars extension like this I want to use dot HBS and I know there's a snippet here somewhere to do that you don't have to search for this you can just copy what I put or you can just get it from the repo so I think I think this is it right here so we need this so we'll go ahead and copy that and let's put that right here so we're setting our view engine and we should be able to use the HBS extension and then I think we have to put the default layout because how will how this works if you've never dealt with a template engine we have a layout that wraps around everything that layout has like the HTML head and body tags and stuff that you don't want to repeat in different views and then it basically just wraps around those views so I think it's default layout let me just check yeah so default layout which I'm gonna call main dot HBS so let's put that in here see the fault layout is gonna be mean all right so that adds the middleware for it so now let's create a views folder and then we have no routes yet we'll do that in a second but I just want to create a views folder and let's create our main layout so to do to create layouts you want to create a folder within views called layouts and then inside layouts we're going to create main dot HBS we're gonna actually have two layouts one is going to be for the login page because it's going to be set up different it's not gonna have a nav bar it's gonna have to be very narrow so we do want two layouts so let's create that as well so we want login dot HBS so we just have two layouts now in main hvs I'm gonna go ahead and just put a boilerplate here just basic you know boilerplate HTML and it'll say storybooks now we are using actually you know what we'll wait on materialized and all that stuff until we actually get this set up now wherever you want to output the view which obviously is in the body you want to put triple curly braces and then body so whatever view we're practically we're looking at it's going to display here so let's now create a route that so that we can render some views so I'm gonna create a new folder called routes and we're gonna start off here with our index J s this is basically any route that isn't followed by something like for instance we're gonna have auth slash whatever that will be the author out stories so when we have story slash something that will be the story's route but if we want something that's top level like dashboard or just the home page we want that to be in this index J s okay so let's start off by bringing in Express because we're going to be using the Express router okay so to create our router we're gonna create a variable and set that to express dots uppercase our router and then before I forget let's just make sure we export this router and then we can set up our routes and I usually like to put some kind of description of what this is so this is gonna be the login say login slash landing page and then the actual route it's gonna be a get request to slash now the way we create a row and I'm sure a lot of you guys know this but we're gonna say router dot get to handle a get request to slash and then we want our function here with the request and response and for now let's just res dot send so we'll just send some text to the client and we'll just say login and then I'll do the same for dashboard that's also gonna go in here so this is gonna be the dashboard oops and it's gonna be a get request to slash dashboard and of course we need to change this all right now in order to use this file we need to go to app GIS and we need to basically link our routing files so here let's say routes and let's do F dot use so anything that's just slash is gonna link to that file we just created which is dot slash route slash index ok so now if I open up a browser and I go to HTTP localhost port 3000 I see login if I go to slash dashboard I spelled it wrong dash board we see dash board now it's not rendering any views or templates or anything it's just sending the text so what we want to do now is create inside views not in the layouts but just in the views folder I'm gonna create a file called let's create dashboard dot HBS and let's also create inside views a full I'm not a folder a file called login dot HBS so in login let's just put H ones for now just so we can make sure that these render so dashboard another h1 okay so we'll save that now in order to render these is simple we just need to go back to our routing our index route and instead of send we want to change both of these to render and just make this lower case this is to and it's going to look for templates or views called login and dashboards now if I go back and reload now we see the h1 if I go to dashboard we see the h1 and it's wrapped in that main layout you can see the title here so let's start to add some of the stuff to the layout like materialized so I'm going to the main HBS lay out and let's search for so you wanna materialize so if we go to get started I'm gonna grab the CDN here and put this here and then we also want the JavaScript the materialized JavaScript what's really cool about this framework is you don't need jQuery like a lot of others so that we're gonna put right here right above the ending body tag I'm also gonna be using font awesome so I'm gonna go to CDN je s comm and search for font awesome and grab the this right I'm actually gonna use the CSS so all dot min dot CSS I'm gonna copy that link tag and put that right below materialized so if I save this and I go back to this page here you can see it's using materialized now we also want to be able to include like public CSS files basically our own style CSS or maybe we want to use some front-end JavaScript or images so basically public assets so we need to create a folder that's gonna be a static folder and by the way you can see these down here these logs this is coming from Morgan just every time we make a request it'll show us where to the the status code and all that stuff how long it took but um yeah so I want to what was I saying static folder so let's go to app J s and we need to define which folder we we want to use as static so I'll go right below here and say static folder so we want to do app dot use and then express dot static and in here we want to put the path to the static folder we want which I'm going to call public now I'm going to use the path module so I can use path dot joined so just bring that in path is just a core nodejs module okay so we're gonna bring that in and then back down here and static we can just do path dot join and the first argument is gonna be double underscore dur name which just means the current directory so an absolute path to the current directory and then we want to go into public that's gonna be our static folder so let's create that so in the route we want to create public and then in here let's create a folder called CSS and then in there let's create a file called style dot CSS and just to test things out I'm just gonna say body background black and then in our main J's file let's include this now since it's in the public folder which we made that our static folder we don't have to put public or anything in here we should just be able to say slash CSS slash style dot CSS same thing with like images or anything like JavaScript files or anything so if I reload now the background is black which obviously I don't want to keep I'm just gonna get rid of that I just wanted to test it out okay so now we have a public our custom style sheet now the login as I said I want that to use a different layout I want that to use the login layout so the last thing I want to do for now in the main layout is just put a container class of materialized container class around the body which will just you know push everything to the middle and then I'm gonna copy everything here and go to login okay this is the login layout I'm sorry this is not this is I know this might be a little confusing I mean you could call this something different but we want to be in layouts login HBS I'm gonna paste that in I'll just say storybooks login and the main reason I'm doing this is because we want to format this page a little different on the main layout we're gonna have a Marshall for the navbar and we don't want that in the login we also want want this to be like a you know a thin little box so what we'll do here is in addition to container let's say log in container that we can see we can style that on our own and I also want this to be in a card class and then card content okay and we'll move the body up into there and that should do it so right now it's still not using that if I go back here if you don't see the card or anything because you have to specifically say that we want that route to use this layout and the way we do that is by going back to our index route file and as a second argument to render we'll pass in an object with layout and we want to use the login layout so now if I go back to the login and I reload now you can see the card and so on okay now I do like I said want this to be really thin so I'm gonna just go to our style sheet style CSS which is being included in both layouts and let's say login container and we'll set the width to 400 pixels margin - let's do 50 pixels I'm sorry margin top because we want to push it down and then I want to align everything to the center so text align Center and four paragraphs I also want to just add a mark a 10 pixel margin on the top and bottom and we're just gonna put the important flag here to override anything with materialized to certain things you have to use this flag to override now if I go back to login and reload now we have the thin box okay so next thing let's let's I guess style this will just style the login page it's not much it's really just a Google button so we'll go now we can close the layouts can actually close both the main layout and the login layout and the stylesheet and we'll go actually close dashboard so now we just want to go into login HBS but not the layout the view so we want to be in this file here so to style this let's see how much do I have here it's not much so I'll just type it out so we want an h3 and I'm gonna have an icon here it's a font awesome and the icon is FA - book - reader and then next to that icon will say storybooks and then let's have a class of section which is a materialised class and then a paragraph with the class of lead and we're just going to say create create public and private stories from your life ok and then we want a class of divider which will just give us like a little border and then another section and in this section we want a button so we want this to have a class of BTN we're gonna make it red and darken - one the link is gonna go to slash off slash Google which is a route that we're gonna create in a little bit and then in here let's put the Google icon using font awesome so it's FA be for a class and then FA - Google ok and then we also want a class of left which will float it to the left and then next to that icon will say log in with Google all right so let's take a look at it we'll reload here and there it is is our login page now I think what we should do next is start to implement the login okay so we want to work with Google OAuth now you are gonna have to create an API key and an API secret so the way that we do this you want to go to your Google Cloud console I don't know the exact URL so we'll just search for cloud console we want to go right here and you need to see you need to have a project so you can see I already have one called dev one if you don't just go ahead and click new project and create one and then we want to go to API and services and then enable api's and services and we want Google+ if we scroll down it should be right here okay so we want to click on that and then we want to you want to click enable mine's already enabled so I'm gonna click manage and let's see you want to go down to credentials this is a little confusing because you have create credentials here but we don't have the OAuth options so we want to click on this link right here and then click on create credentials and you should see ooofff client ID that's what we want so we'll click on that the application type is a web app you can name it something else if you'd like I'm just going to keep that and then down here under the redirect URIs you want to add your callback URL which is going to be HTTP localhost and once you deploy you'll have to change this or add a new one so it's gonna be three thousand and then it's gonna be slash off slash Google slash call back okay that's gonna be our redirect URI our call back and then we should be all set we don't shouldn't need anything here and let's just click create and then once we do that you get a client ID and a client secret so really important we want to copy this and we're gonna put this in our config okay so this is gonna be our Google underscore client underscore ID okay and then let's grab the secret so I'm gonna copy this one and let's say Google underscore client underscore secret and we're gonna paste that in and don't use mine cuz it's not even gonna work after this alright so click OK and we should be all set as far as being able to work with Google now we're going to be using passport for authentication if you've never used passport it's it's an incredible authentication package and there's just tons of what are called strategies so 500 plus strategies and just different ways to log in so you know tokens local so local username and password I do have like an hour-long video on YouTube showing you how to use passport local Facebook Twitter auth0 github what we're using is this right here Google OAuth 2-0 dart 20 so if we go down here it kind of gives us an idea of what we need to do we need to create what's called a strategy so we want to create a Google strategy and pass in our client ID our secret our callback URI and then we have this function where we get we get access to the access token and the profile data so the profile data will be like your your name your Google image whatever you use is your you know your main Google image what else you are I think it's your first name last name display name stuff like that and that'll be in the profile so in an ID of course so what we're gonna do is save the user in our database okay once they verify with Google we'll save that stuff in our database and then they can use that as a login all right and then the routes we're going to need these two routes off Google and then we just call our Google strategy and then our callback which again we use our Google strategy and we set a failure so if it you know the don't log in correctly it'll redirect them here if it's successful it'll redirect them wherever you want so that's the basic idea so we need to create a passport config with our Google strategy so let's jump back into vs code and make sure this is saved and then I'm just gonna restart the server since I added new global variables and let's create so in our config what I'm gonna do is create a file called passport dot J s so this is where our strategy will go now in our app J s we want to bring that we want to require that file we also want to bring in passports so up here at the top let's say Const passport equals require passport and we'll go right under the config here and say passport config so we want to require dot slash config slash passport now we want to pass in because we can actually pass in as an argument the passport that we just brought in up here so that we can use it in this file and then another thing we need to do here is add the passport middleware so I'm gonna go down right under we'll go right under handlebars here and we want to set the passports middleware so app dot use and this is all in the passport Docs so dot initialize and then we also want passport session now in order for passport to work with sessions we need to implement Express session which we installed in the beginning I believe yeah so we have Express session installed so let's go ahead and bring that in say Const session equals require Express session and then this has its own middleware that we need that we need to implement and we want to make sure that we put it above the passport middleware so we want to put it right here ok and I forget exactly what it is I'm gonna go to Express session so the middleware for this is right here so app dot you session and then we can just include a couple things so I'm gonna grab that and put that right here so secret you can use whatever you want resave I'm gonna keep it false this just means that we we we don't want to save a session if nothing is modified and then save uninitialized I'm actually gonna set this to false which means don't create a session until something is stored and we don't want this because this won't work without HTTPS now later on we're gonna put a store value here which will be our store so we can store them in the database but we're not gonna do that just yet so that should be good for now just make sure that this is above this so we'll save that okay we're gonna give it a little error here just because we haven't done anything in this config file so let's go to our passport config and first thing we want to do is bring in we want to bring in that passport Google OAuth 2-0 module and we want to call this Google strategy so set that to require passport - Google - Oh auth - 0 and then we just want to tack on to that dot strategy and we're also gonna bring in Mongoose oh you know what we didn't create shoot we didn't creates our user model yet because we're gonna be dealing with the database here with users so we need to have a user model so I think what we'll do is just save this and let's go ahead and create that real quick just gonna close those up and we'll create a folder called models and inside here we're gonna have a user dot JS file singular with an uppercase u it's just the convention for models and this is really simple if you've ever used Mongoose what I'm doing here we're gonna bring in Mongoose would like am i doing I'm gonna bring in Mongoose and then we're gonna create a schema so user schema and we want to set that to new Mongoose dots schema and then we pass in an object with the fields that we want for the user now remember I told you we're gonna get certain fields back when they authenticate with Google one of those is an ID so I'm gonna call this Google ID which is separate from the you know the object ID that monk MongoDB gives you by default so this is gonna be type string and we'll set required to true all right and then I'm just gonna copy this down just two three four five okay so we have Google ID the next one is gonna be display name so display name is gonna be obvious we need a comma here for all of these so display name is the first and last name together so Google gives you that back it also gives you the first and last name which I'm also gonna save I mean you don't really have to if you don't you could just do the display name or just the first and last name but I'm just gonna get everything it gives us and then the image so it also gives us an image this is gonna be yeah that'll be a string as well I'm gonna take required away from that and then let's just add a created at so created at is gonna be type date and we'll set a default of date dot now which will just automatically put the date and time in so that's the stuff that we'll get back from Google and then we want to export this so modulus sports we want to export this as a mongoose model so we need a model name which is user and then the user schema should be passed in and now since we have the model we can go back to our passport config and we can bring the user model in so dot dot slash models slash user okay we should be able to use you know interact with our database and then we need to module dot exports and we're going to export function now remember we passed in passport into this file so we can catch that here okay i remember an app j s right here we passed this passport in so we're bringing that in right here and then we want to create our google google strategy so the way that we do that is with passport dot use and here we can say new google strategy and this takes in an object with our client ID let's do our ID first so client ID which we can get from process dot e NV dot Google client ID ok then we also want the secret client client secret and then also the callback URL so the callback URL for us is going to be slash off slash Google slash callback now the next thing we want here is a function so we want to go right after where we have this object and put a comma and then we're gonna have a function so I'm going to use I'm going to use a sync away because we're dealing with Mongoose so let's go ahead and just create an arrow function and this is going to take in the axis token so we're gonna get this stuff available to us the Refresh token I was refresh token and then the profile which is what's important to us here and then done which is the callback that we call when whenever we're done doing what we want to do so for now I'm just going to console.log the profile now there's a couple other things we have to do in order for this to work we need to put the serialized user and deserialize user methods in here so if we go to passport js2 the docs just search for serialized so right here these subsequent requests will not contain credentials but rather the unique cookie that identifies the session in order to support login sessions passport will seat serialize and deserialize the user instances to and from the session so these two lines or they should say these two blocks of code here we need to put these in here and we want to go outside of you know we want to go outside of passport use that ends right here you just want to make sure you're still within the exports and I think yeah so it's just calling the callback passing the user ID in I'm just gonna make this an arrow function though just to stay consistent down arrow function make this just to clean it up and since this is an arrow function we don't actually need curly braces here or here and wait a minute that's not right they don't need I do you don't need this one or this one all right so just looks a little cleaner so now we need to before we do anything here this is I mean we're just logging the profile data we will ultimately we want to save it in the database but I just want to set up our routes so that we can actually use this strategy so to do that lets go to routes and let's create a new file called off dot J s for any authentication routes and I'm just gonna copy the index routes for now and let's see so we want this one here so this route is gonna authenticate with Google so it's gonna be a get request to off slash Google so here we just need to do slash Google because we're going to link it to off in the app J s and then we can we can actually get rid of this whole function right here because what we want to put in here is passport which we'll have to bring in dot authenticate and then we need to put our scope in here because there's there's different scopes of well actually we need to put the strategy first which is Google and then the scope actually how does this go I have to check this out let's see we just search for passport dot authenticate so that's for local actually let's go to the specific strategy Google so this goal is like this so scope profile so basically we're just asking for profile data so we'll just copy that and put that right in here like that and that should do it and of course we need to bring in passport okay that's that now the second route that we want here is the callback so say Google callback and this is gonna be get request to slash off slash Google slash callback so here we want to put Google slash call back and then again we can get rid of this function we don't need that we just want to call passport dot authenticate and just spelled that wrong it's a passport authenticate and first argument is the strategy which is Google and then second will be an object and we can specify a failure redirect so what do we want to happen if it fails we want it to redirect to slash which is the login and then we want to go after the passport authenticate which ends right here and put a comma and then we have our function request response and all we're gonna do is rezzed our redirect so this this is if it's successful we want to redirect to the dashboard okay so hopefully this is clear to authenticate we're using our Google strategy which we created in our passport j/s file and we're just saying we want the scope of whatever's included in the profile and then this is the callback that it's gonna hit if it fails it's going to redirect to the root and if it passes it'll redirect to the dashboard okay hopefully that makes sense and then of course in our app j/s we need to bring that route file in so or is it where'd I put the routes right here so I'm just gonna copy that down and let's say off any route that his slash auth is gonna be linked to that so we shouldn't have errors down here just keep going down okay so it looks like we still have some kind of error here requires a client ID option I did put in a client ID oh I forgot the T okay so no more errors you guys probably saw that while ago so let's try it out and what should happen is we should just console.log the profile did it should it should show us the the page to you know choose a Google account or whatever but let's let's just try it out so I'm gonna go to the login and just reload so this is the root the home page log in now if you have one Google account I believe it just uses that you know obviously if you're logged into Google if not you'll have to log in but if you have multiple accounts as I do you should have an option here so I'm gonna use just Jennifer chien account now this here is gonna hang because we never called the callback within our Google strategy however we did console.log the profile and that's what I want you guys to see so what we're getting here is an ID or get a display name we get a name with an object with family name and given name and we get an array of photos with one object with a value so this is your the Google photo of this user and then the provider and then just some raw data down here it's same stuff so what we want to do now is take this here that we're getting from Google after a successful authentication and store it in the database so let's do that now I'm just gonna click back on this so that that doesn't hang and once we do that we need to call this callback which is done I called it done you can call it CB or whatever you'd like so let's continue on with this so we brought it we already brought in our user model so we should be able to use that let's get rid of this console.log profile and I'm gonna construct a new user object so we want Google ID remember these have to match up with our model our schema that we created our user schema now we can get this from profile ID so profile gives us everything I just showed you down here so we want the ID we want the display name so profile now the display name is a direct object so we can just do that first name we have to use name dot and then what was a given name and last name was in an object called name and that was what was that family name and then the image so the image was in profile dot photos which is an array so we want the first item and then we want the value okay so dot value so that will give us the new user and then we can go down here and let's open up a try-catch and we want to try to store the user so first we're gonna fun look for the user so let's say let user equals a weight because we're using Mongoose here we're going to use the user model and call the find method and our let's do find one and we want to find where the Google ID is equal to the profile dot ID to see if that user exists if that user exists then we want to call our callback know which I believe is the error so null for the error and then pass the user in okay else so meaning if there is no user then we want to create one so I'm going to set this user variable to a weight and then take our model and call create and pass in the new user object that I just created and then we'll call done with null and our new user okay I don't know I just said it like that and our new user come on down so for the catch I'm just gonna do a console error of whatever that error is and that should do it so we should be able to authenticate now and it should get saved in the database so let's go back here and reload and click log in with Google I'm gonna click Jennifer so we get redirected to the dashboard which is exactly what should happen but let's go to our database and we should be able to check out our data by going to collections so we have our story books database with users collection and there it is so we have a Google ID display name first name last name image created at so we're now able to log in log in slash register with a Google account now let's see what should we do next here trying to think of I want to do this so let's let's make it so that we can protect routes because we shouldn't be able to go to the - actually you know what let's do let's do the the the navbar we probably should have already done that so we can close up passport j/s just in the XJS it can close that up you know what let's create our logout which is actually really simple so with logout let's create a description logout user user and the route the route for this will be slash off slash log oh all right and then we'll see router dot get slash log oh now with the passport middleware once we log in will have a logout method on the request object so we can just simply call that request logout and then after we log oh let's just redirect so resident redirect to the home page and that should log us so now I want to have a navbar or it's actually gonna be just like a hamburger menu that has the logout link along with the dashboard link in the public stories link so let's go to our views and this is actually going to be a partial so I'm gonna create a new folder here called partials and in here I'm going to create a new file called underscore header dot HVS and the underscore is because it's a it's a partial it's being inserted into another view so for the header here I'm actually just gonna paste this in you guys can just copy it it's just a nav tag with a couple color classes nav wrapper container we have the logo which is centered and then we have a link here with the data target of mobile demo and then this ul has an ID of mobile demo so when you click on this which is the bars icon basically a hamburger menu it will open this up now I'm gonna save this and go to my main layout and I want to insert this so I'm gonna go right above the container and we can insert a partial like this okay I believe is it - we just double-check that yeah so that should insert the header so I'm gonna save that and then we'll go back now we want to go to the dashboard and reload and there we go now this if I click this it doesn't work because with materialise if you want to use this sliding nav bar you actually have to initialize it in the JavaScript so we want to go to the main still in the main layout let's go under the under here and put in some script tags and since we brought into materialized we can use this MDOT and then side nav and we want to call an it and then we need to select the class so document query selector and it has a class of side now so dot side now so this should initialize it and then if we go back here and reload and I click that there we go all right now if I click logout this goes to auth logout and remember that route logs a cell and brings us back here now if I go up here and I type in dashboard without logging in I can still go to it which I don't want so we're gonna create a piece of middleware to make sure that we can't do that also if we are logged in I don't want to see this page I want to be booted back to the dashboard so this is actually pretty easy I'm just gonna collapse these and create a folder called middleware and in here we'll create off j/s so this is off middleware and middleware is just a function that has access to the request and response objects so I'm gonna module dot exports and let's create an ensure off function and this is gonna take in request response and then next next is just the function you call when you're done doing whatever it is you want to do to call the next piece of middleware and we can check here we can say if and then on that request object we have in is authenticated method so we want to call is I spell that right then to kated if for quest is authenticated then we want to just return next and move on because I mean they're fine they can you know keep going because they're logged in else then we want to redirect to redirect to the select to the homepage to the login okay so that's ensure off now I also want to create an insurer guest for the you know if you're logged in and you try to go to the the landing page I don't want them to see the login so this safe function request/response next and again we're gonna check to see if request dot is authenticated okay so if they're authenticated then we want to redirect say res dot redirect to the dashboard and else so else then we want to just return next alright so we'll save that now let's go to our index route so routes index J s and let's bring those in so I'm going to use D structuring here and just pull in off the hell did I call it and show off and show off and ensure guests require so we want to require that from dot slash wait where are we we want to go up one level to middleware and then off and then whenever we want to use middleware within a row we just added as a second argument so we'll put in here this is the login which should be ensured guest because only a guest someone that's not logged in should be able to see this however the dashboard is ensure all right so save that let's try it out so now if I try to go to dashboard I can it just boots me back but if I log in with Google I'm at my dashboard and if I try to go back to just the login page I can't so we have that middleware now it's pretty simple and we have protected routes now we do have a issue here so I'm logged in right now in my dashboard and actually you know what I'm gonna do is in the dashboard rrrow so we can get the user with request dot user so I'm gonna console.log here quest dot user like that and then if I go back to my dashboard and I reload so it kicks me out cuz I'm not logged in actually I didn't even have to do the console log because we already have the the protected routes but you saw as soon as I reloaded my server I got kicked out like I can reload now and it'll actually console.log my user down here let me go down to the bottom so you can see it logged my user if I change anything here like just get rid of this console log and then I save it resets it restarts my server using node lon so now if I go back and I reload I get kicked out so I want to prevent that from happening and what I'm gonna do is store the session in the database and we're gonna do that with that connect extension or not extension package that we installed so let me just close up what I don't need here and let's go into our app J s so in our app J s we're gonna go up to the top here and bring in let's say I'm gonna call this store and we want to set this to require and we're using connect now we we just want to add on to this we want to pass in session so this session right here because you need express sessions or some kind of session middleware and we need to pass that in here so just make sure that's below it and then all we should have to do is go down to Express sessions where we have that Express session middleware and add in a store and set this to a new store and then this takes in an object with the Mongoose connection and to get that we can just simply take Mongoose which I don't think we brought into this file but we will and then dot connection because that'll give you your current Mongoose connection so let's make sure we bring that in I'll just go just put that in up here all right so now let's save that and we're going to try this again let's reload and login so now I'm logged in and I'm gonna reload the page and notice it didn't boot me out and that's because if I go to my database and I reload here you can see sessions and there's our session so the cookie and if we just see this right here so Passport so it just shows our cookie it shows passport shows the user ID for this login all right so that fixes that issue so the next thing I want to do is let's what do I want to do next so we have our dashboard a dashboard has nothing on it I think we're ready to just to start dealing with stories I think our authentication is pretty much all set one thing I would like to do is in for our dashboard row let's see so rendering dashboard well it's actually passed in the user name here so dashboard and then we'll pass in name with request dot user dot and let's pass in the first name and we should be able to access that in our dashboard in our view so I'll go to views dashboard and let's see let's actually change this to an H 6 and then we'll have an H 3 and we'll say welcome name and we'll put a paragraph and say here are your stories because we're gonna want to list all of our stories here but let's see if that name shows up yep there we go okay before we do like the dashboard stories I would I'd like to just do the general general Staal know we can do the dashboard stories first let's do that get that over with so in our index J s well actually before we do this we need to create our story model because whenever we're dealing with a new resource and the database we need a model for it right so let's go to models and let's create story Jas and I'm gonna just copy my user schema here paste that in and it's changed this to story so for our story we're gonna have a title which will be a string we'll set trim trim any white space to true body status now status is gonna be either public or private so I'm gonna set a default for this to be public okay and then I'm also gonna set an enum which will be a list of possible values so it can either be public or it can be private what am i doing public or private and then we're gonna actually have a user connected to each story we need to know who who did what so the user is going to be a special type of an Mongoose object ID so Mongoose dot schema dot dot object ID and then the way we connected to the user model is with ref so or have a reference to the user model and then the last thing I want is just to create it out so we can get rid of that image okay and then we can just change this to story and we want to pass in the story schema and that should do it so let's close that up and let's go back to our index route here because what we want to do is get all stories that are our own now I know there's no stories in the database just yet but we'll just add the functionality and it'll just say whatever you know we know stories available so in dashboard let's first bring in our model so in require okay I want to bring in story and then right before we do the render let's do a try-catch and let's say Kant's stories equals and we need to make sure this is a sink so stories and then we're gonna await story dot find and what we want to fund what we want to limit here is the user we want to limit it to the logged in user which we can get with request dot user that gives us all the fields but we want to match the ID now you have to in in order to pass in data to a template into a handlebars template and render it loop through it and all that we need to call dot lean and this actually will tell us what this does so documents returned from queries with the lean option enabled our plain JavaScript objects not mongoose documents and that's what we need in order to pass it in and use it in a template okay so now what we want to do is take this render and we want to move this up here after we fetch the stories and we want to just pass in stories okay now on the catch I'm just going to do a console error of the error now we need to figure out how we want to handle errors and we're not using like you know react or anything so we can't just simply send a JSON object or anything like that we're gonna render an error templates so under views I know we're kind of bouncing around here but under views I'm going to create a folder called error and then let's create a file called 500 HBS and we'll create a file called 404 HBS and you can do other errors if you want but here I'm just gonna paste this in it's just 404 not found we're sorry and then a link to the dashboard and then in the 500 we'll go ahead and paste that in just server error okay so now we have those templates and now if this if something goes wrong here will res dot render and let's render from the error folder will render 500 all right now if we go back to dashboard we're not gonna see any changes and what I would like to do is loop through the stories although I know there's no stories in there yet and then I'll put them in a table alright so let's go back to our view dashboard HBS and from here I think first what we want to do is is actually check to see if there are any stories so we can do that by saying if stories so if stories then we'll have the table and all that else and then we want to just end the if so else then we'll say you have not created any stories which is what we should see now let's reload okay so you have not created any stories now to loop through the stories which we don't have well actually we're gonna make a table so let's first just construct the table so we'll say table or in give it a class striped and then let's create the heading so the tea head with the table row of table headings of title so I've title will have the date and will have the status and then this is just for like the delete edit and delete buttons and then underneath the tea head let's create the tea body and to loop through those stories that were passed in we can say each stories and just close that up so each stories and then we'll have table rows with that data so let's say table row TD so our first column is going to be the title which is gonna actually be wrapped in a link that goes to slash stories slash and then the story ID which would be underscore ID now it automatically knows that this this is the story ID because we're in or is it we're in the loop here so we don't have to do stories dot underscore ID it just it just knows same with the title so we can just put in a title like that we don't need stories dot title and then the date so the date I'm gonna want to format this using a handlebars helper but for now I'll just put it right in there to create it out okay and then we want the status so here I'm gonna put it in the span with the class of - status and status like that and then this last one here is going to be the edit button and the delete now you can't have a delete get request so we can't just have a a link to delete we'll have to use a form and we're gonna use method override so we're gonna do that later I'm just gonna leave this blank for now so if we save this we're not gonna see any difference because we don't have any stories created so I think what I want to do next and feel free to I mean this is a long long video so feel free to stop and come back later if you want but what we're going to do now is create our add button we're gonna have a little add story button that goes to a forum so we can actually create a story so let's create the button first which is going to be a partial so I'm going to go to partial new file underscore add underscore BTN dots HB s and let me just grab that real quick so pretty simple you just have fixed action button as our class it's gonna go to stories add as an icon and so on so if we save that we go to our main layout we can insert that just like we did or is it main layout just like we did the header so you just actually copy that down and just say add what does it add underscore BTN so now if we go back and reload we have a little add button here if I click it it goes to story slash add which doesn't exist yet so let's create that next just close that up close that up for now so under views we're gonna have a few stories views so I'm gonna create its own folder so in views will have a stories folder and let's create a file in stories called a DHBs so this will be our add form and I'm just gonna grab this because it's not it's not much it's not difficult so it's just a form it's gonna make a post request to slash stories and we have a title has a name and ID we have a select field here we actually don't need unpublished so just public and private public is selected by default we have the body which is a text area but I'm going to show you how we can implement something called ckeditor and then we have a Save button and then just a cancel that goes to the dashboard so let's save this and let's create the route to show this so we need two stories route file now so under routes say new stories dot J s I'll just copy what we have in index for now and let's see we'll just get rid of the second one here and we'll keep we don't eat ensure guests because the login or the home is the only thing that's gonna need that who will keep ensure off we'll keep the the story model and then let's see so this first one here is gonna be show ad page so it's a get request to stories slash ad so let's put that here so just slash ad here ensure off and then we just basically just we don't really need to pass anything in we just want to render that template cuz we can get rid of that we're gonna rend our stories slash ad so pretty simple now in order to use this file we need to just like the others we need to bring in the route to the app J s it's changed this two stories okay so I'll link that and now if we go back and we click on this it brings us to the page now this looks really weird the select isn't even really showing up because with materialise you have to initialize it with JavaScript just like we did with them with the menu so let's go back to the main layout and right here this an it I'm just gonna copy this down and we want form select in it and we're gonna grab query selector and we want the ID of status which is that select form so now if I go back and reload nothing what did I do wrong oh I get an extra parenthesis okay so now we have our select list so for the text area here I want to make this into a WYSIWYG editor we're gonna use something called CK editor which we can get the CDN for from CDN j/s so if we search for CK editor right here we're gonna grab not this jQuery adapter thing but this right here CK editor j/s I'm gonna grab the script tag and I'm gonna put this in my main layout here I'm gonna put it right under the materialise and this is actually really easy to setup or initialize we just need to go down here and say CK editor dot replace and in here the name of the field we want to replace which is body because if we look in our ad right here text area has a name of body so we want to replace that with this with the CK editor okay so back here replace and then we can have an object with a plugins value because there's a lot of things you can add if you want to look at the docs you can but we want the WYSIWYG area so what you see is what you get area we also want the toolbar and basic styles and link we want to be able to do links so let's save that and let's go back close this up and close all this let's go back here and reload and now we have an editor now if you put something in here and you hit enter it'll automatically put paragraph tags in so it'll format it automatically now we want this to work remember this is making a post request if we go back to the add template it's making a post request to slash stories so we want to create that so let's go back to our stories route and let's see let's just go right on I'll just copy this so let's say this is gonna process the ad form and it's gonna be a post request to stories so we want router dot post to just slash which represents stories and ensure off and get rid of this and we want to try catch for the error I'm just gonna console error error and then res dot render error slash 500 now in here we want to process we want to create the story so remember a request dot body if you've worked with express you know this request dot body is gonna give us the data that's sent in from the form however in order to use your quest dot body we need to add a piece of middleware which we haven't done yet so in our app J s file let's go right after we initialize this and we need to add the body parts or middleware which is app dot used to accept form data we want to do express dot URL encoded and we're just gonna pass in this extended and set that to false and then I'm also going to accept JSON data although we're not doing that but I'll just add in Express dot Jason so now we should be able to get the data from request body now this obviously this doesn't include the user and the user is part of the story schema so we can get the user from request dot user so what I'll do is add on to this a user value and set that to the request dot user all right our request our user ID okay so yeah so that'll get added to that and then we need to create it so we need to await let's make sure we had a sink up here so we want to await and we'll use our story model and we want to just call create and we're gonna pass in request dot body so that'll create it and then we just want to redirect to let's redirect to the dashboard alright so we'll save that and then let's go back let's reload this add page and I'll say let's say gen story one and public and then I'll just say this is my story I hope you like it actually you know what let's make this a longer one I'm just gonna grab some text now if I copy it right from the website it's gonna format it for me so I'm just gonna paste it in let's see let's paste it in the text editor I'm just going off screen here and I'm just gonna paste it in sublime text real quick and then copy it again just so there's no formatting to it and then I'll paste that in here and hopefully that gets rid of any formatting I mean usually you're not gonna pay stuff in here but we'll go ahead and save this so we get redirected waits something went wrong okay so we had some kind of error here let's check our console validation field status public status was undefined wait a minute just ask me okay that should've got sent public is not a valid enum value did I mess something up in the model oh well we can see that the that that's working all right let's try that again there we go so it's submitted and now we're seeing it on our dashboard so we see the title the date which I want to format and then public okay and if we go to our database stories and there it is so yeah so it's formatted with paragraphs which it should be but it yeah it shouldn't have any styling or anything good and the user is that's the user ID right there so now we're able to add stories let's just add one more I'll say Jen's story too okay and now I just want to check to see if I log out as Jen I log in with a different Google account welcome Brad you have not created in these stories good and I guess we'll create one for Brad okay good so I think the next thing and that I want to do is let's see you know what let's format this date and we're gonna do that using moment GIS which we already installed and we're gonna create a handlebar helper and every time I say that I think of hamburger helper but we want a helper that we can wrap around the date and format it so I'm going to show you how to do that so we're gonna create this just close these up and we're gonna create in the root folder called helpers and then inside helpers will create a file called HB SJS so any hamburger helpers and the handlebar helpers that we're gonna create will go in here now I want to bring in moment because we're gonna be using this to format the date and then we'll just do module exports and we'll just have a bunch of functions here so the first one is format date okay so this is gonna be a function that takes in the date and then whatever the format that we want and then we're just gonna return from it moments so we're using moment here wrap wrap the date that's passed in and then with roll moment we just say dot format and then whatever the format is so for that one's pretty simple so let's save that now in order to use this in our template we need to register it with handlebars so we need to go to app J s and go down to I'm gonna put this right above handlebars so we first need to bring it in so it was called what format date actually we're going to use D structuring here because we're gonna have a bunch of them so format date require helpers HPS and then we need to add it down here so in this object that's passed into this exp HB s we want to add helpers and then that's an object just make sure you put a comma after it and then we want to put in our format date like that and then we can go into our dashboard and this created that we can put format format date and we don't need to put parenthesis so just a space and that's passing created at in as this date now we also want to pass in the format so we want to put a space after created at and whatever the format we want so I'm gonna go ahead and just put in some quotes and then paste the format that I want so I just want the month day year and the time so we want to put this in and you can look at the moment moment Jayesh documentation if you want to use some other type of format so let's save this and let's go back and now you can see we get June 18 2020 gives us the time good so the next thing I want to do is the public stories so we have this link it goes to just slash stories so it's a get request to slash stories and this is where we want to show all of the stories that are public so let's create let's see we'll go ahead and create the view so let's go into views stories and I'm gonna create a new file in here and call this index HBS so this is our stories and we might as well just add this HTML while we're at it so let's put an h1 here stories and we're gonna have a class of row and what we're gonna want to do is loop through the stories because when we create the row we're gonna render this template and we're gonna pass the stories in so let's say each stories and what's cool about this with these with the each is you can actually put an else meaning if there are no stories so we don't have to put an if beforehand so let's just end this with each and in the else we'll just put a paragraph that says no stories to display and inside each hair we're gonna want to style basically cards that hold the story the title and at least a portion of the body so I'm gonna put a class in here of call s 12 and then M 4 so we're gonna have three three four column divs and for each one will render a card and I'm gonna have a class of card image we don't have images but what I'm going to put in here is an icon if it's if it belongs to the user that's logged in so we're gonna have to do some trickery here and we're going to use a handlebar helper for this so I'm just gonna for now let's just put a comment here and let's say to do and it's icon because that's gonna be a helper now we're gonna go outside of the card image and do a card we want card content and then I also want the center-aligned class here and inside here we'll have an H five with the title so we're in the each loop so that's going to give us the story title then we'll have a paragraph and inside this paragraph we want the body now we're gonna have we're gonna have some issues with this with the body for one it's going to be way too long for the you know from the one where we added the long text so we're going to need a helper for that also it's going to render the paragraph tag so we want to strip those out as well so we do have some work to do on this after but I'm gonna save that for a little later so after the that paragraph let's put in a line break and then we're going to use the class of chip so I materialize a chip is kind of like a badge it has a background color and it has some rounded corners and this is where I want to put the user image and their name so let's put an image tag and we're going to get the image from the user so story dot user dot image because remember we have access to the image I'm sorry the user fields because of that relationship that we created within the model and we're going to populate that populate the stories with the user data and then we'll have a link so this link is gonna go to slash stories slash user slash and then we want the user ID so we're gonna say user dot underscore ID and what that route is gonna do we haven't created it yet but when we do it's going to show all the stories of a specific user and inside this link will just show the display name so user dot display name alright and now after the let's see we want to go outside of the card content so that ends here and we want a card action class and let's also actually add the center line so this is gonna be the read more button so let's say a class BTN and we'll make it gray and this is gonna be this is gonna go to slash stories slash and then the ID of the story so underscore ID cuz it's just gonna go to the single story page so here we'll just say read more and I think that should do it so we'll save that just go down here make sure we have no errors so we'll save that and then let's go to our stories routes doesn't want to go to routes and then stories J s and we want to create the route to a get request to slash stories just to show them all so let's go down here I'll just copy this okay so we'll copy that and we'll go right here say show all stories so there's basically two things we want to do here we want to fetch the stories and we want to render them in the template so the route is gonna be a get request to stories ensure off you want to make this a sync since we're dealing with with Mongoose or the database and it'll just get rid of this for now so we want to try catch and for the error same thing we've been doing we'll just console.log the error and we'll go ahead and res dot render error slash 500 now on the try we want to fetch the stories so we want to wait take our model and call find and we're gonna get all the public stories so let's say we're a status is equal to public and then I'm gonna populate so I want to add on to that populate with the user models because I want that user data like the name and stuff like that that's not part of the story that's part of the user model so we want to make sure we populate it with that and then we can sort so I'm just gonna go ahead and sort by created at so created at let's say descending ascending and then we just need to add that lien so that we can pass it into our template okay now we'll res dot render and we're gonna render the template that we just created which is stories index and we just want to pass into that stories okay so we'll save that now we already have our menu that goes to slash stories right here so let's click it and that didn't work Roeder get this should actually be just stories oh we don't need to put stories in here it's just slash because I save that because in our app J s where we link all of our routes you already have slash stories so let's try that again let's reload and let's go public stories and there we go now right off the bat you can see the problem here so this one I mean these are just short text but this body has a bunch of text in it and it's just way too long also it's showing us the tag so I'm gonna create two new helpers one is gonna strip the tags out and one is gonna truncate the text so let's jump back into our helpers file which is this HBS file and I'm actually gonna paste these in because I don't feel like typing them out so the truncate so truncate function it takes in a string and it takes in the length so basically how how much how long do we want this to be 150 characters 200 whatever and then this logic here is just gonna cut it to that length and it's gonna add on this ellipses ellipses is that what this is called three dots it's gonna add that on as well okay so we're just doing that with the substring method and then we're just returning that new string now for the strip tags I'm going to create this helper so this strip tags takes in an input and then it uses dot replace and regular expressions to just replace any HTML tag so this is just a regular expression that's going to look for anything with the front and back brackets angle brackets and replace it with nothing so that's all that's doing so now we should be able to use strip tags and truncate although before we can use them we have to register them so we're going to bring them in from the helpers file so we have strip tags and we have truncate bring those in and then add them here strip tags truncate and now we should be able to use them so we'll go back to or is it index HBS and go down to where we have the body so the body is right here and what we want to do is wrap this in both of those tag both on sorry both of those helpers so the first one is strip tags and then the way we can use both of these is with parentheses so if we put parentheses here we can then inside here pass in truncate so it's gonna strip tags and then it's gonna what's going to truncate at first so we start within the parentheses and then it's gonna strip the tags from that so let's save then let's go back and reload all right so it's stripped the tags but it didn't truncate and the reason it didn't is because we didn't input the second argument if we look at truncate it takes in the length and we didn't put that in so let's add right here 150 okay so reload that and now we get a max of 150 characters it adds the dots on the end and that's that alright so that looks good now I think the next thing I want to do since we're we're doing the helpers here is add the Edit icon helper so I'm logged in as Who am I logged in as logged in as Brad so if I look at public stories my one story here should have an edit icon so let's go back to the helpers and for the edit icon let's see do I want to paste this in or I guess I'll just paste this in you guys can copy it so and I'll just explain it so this function here it's gonna take in a bunch of things it's gonna take in the story user okay because it needs to know what the user of this particular story is it's looping through all the stories we need to know the user for each one we need to know which user is logged in and looking at the stories we need to know the story ID and then floating is just so we can have an edit icon somewhere else because with these cards it's what caught the icon or the button we want to put here is actually a floating a floating button in materialise when we view the single story we want an edit button as well but we don't want it to have those floating classes so that's why I have floating here and I just have it set to true by default and then here we're just checking the story users ID we're just converting it to a string and then we're seeing if it's equal to the logged in users ID and then if it is we just continue to check to see if it's a floating icon or floating button whatever you want to call it if it is then we just have these classes here okay if it's not then we don't even have any classes on the link it's just wraps the icon and the link is going to go to the edit page with that particular story ID okay hopefully that makes sense and if it if the logged in user is not the story owner it's just going turn nothing and it's we're not gonna see the icon so let's save this and then let's go back to apt js' and bring in edit icon and register it down here and then we'll go back to our index HBS where I have this right now I just have a comment there so we want to get rid of that and we're gonna pass in and it's edit icon yes we have edit icon and then we're passing in user now this is a little tough to understand so if I just say user it's gonna pertain to the story's user okay because we're in the loop and that's what I want right I want the story user if we look at the function here that's the first per am the next one is the logged in user now in handlebars if I just go up here and say user that's gonna be the logged in user because I'm not within this loop now if I want to access that user from within the loop what I can do is a dot dot slash so basically I'm saying go up one level out of this loop and grab the user so that's going to be the logged in user the next parameter that I want to add in is the story ID which I can get with underscore ID because we're already in the stories loop there's also a floating option but I want it to be true in this case and that's what we have as the default so we don't have to you know explicitly set that so let's save this it this might not work but yeah okay so well that's not what I expected but cannot read property idea of undefined no that is what I expected so I don't think it's we don't have access to this global user we have access to request dot user within our routes but in our template it doesn't know what user is it knows what this one is because it's in the loop so what we're going to do is go to our app J s and I'm going to set what's called a global variable not like an environment variable but an express global variable so we'll go see we'll go like go right under the passport here it's a set global variable so an express the way that we can set this is first I mean we need to set it as middleware so app dot use and then we're gonna pass in the function just a middleware function which has access to the request and response object of the cycle and next which we need to call after we're done and what I'm gonna do here is set a global variable by saying res dot locals dot user and I want to set that to request dot user because with the passport with the authentication middleware we have access to request user we've been using it in some of our routes so this is a middleware function so we have access to it here and what I'm doing is setting a global variable so doing this should allow us to just access user from within our templates alright so we'll set that and we'll set it to null if it doesn't exist and then we just need to call next so now if I save this I'm hoping this will now work and it does this is not right but we're not getting an error what did I do here oh you know what we have to actually use triple or is it index for edit icon we have to use triple curly braces here in order to parse that there we go so basically if you want to parse HTML with handlebars you need to use that triple that triple curly brace so this edit icon does look a little big and you'll notice in the helper here I have a class of F a small that's not an actual like font awesome class that's something that I'm just gonna add to our public CSS here so let's say F a - small and I forget what size I made this let's give it a width width font size of 16 pixels then we're gonna add important and reload okay so now it's a little smaller so now we have an edit button on whatever story is ours if I log out and I log back in with gens account and I go back to public stories now the Edit icon is on these two because these are these belong to me now if I click edit obviously this does nothing because we don't have that wrote yet so I think that the next thing we should do is create that that functionality so we do need to render an edit page here so I think what we should do first is go to our route stories routes and create a route to show the Edit Page now when we show the Edit Page obviously we have to pass in that particular story because we want to show the values within the Edit page or within that forum so let's see let's copy this okay so we want to show the edit page and it's gonna be a get request to stories slash edit slash and then the ID of that story so here it's gonna be edit and then the ID parameter ensure auth and then we want to let's mark this a sink okay and then here we want to say Kant's story we want to wait on story dot and let's use find one we're finding one story and we're gonna get it by the ID which we want to match to request dot params ID and then we just want to add dot lean because they're gonna be passing this into our template now we want to check to see if the story is there so if not story oops if not story then let's go ahead and return res dot render and I want to return the 404 error page so error slash 404 okay and then we'll keep going down here now we also another thing we want to do is redirect if it's not the story owner because we don't want someone to be able to get we don't want Jen to be able to go to Brad's edit page and edit it or even see any of that so let's go ahead and say if we'll say if the story from the database if the story dot user is not equal to the request dot user dot ID which is the currently logged in user if that's not true then let's redirect and we'll just redirect us to this public stories which is just slash stories okay and then actually it's else here else then we're gonna res dot render the story / edit and we of course want to pass in the story so it's saved that now we need to create this stories edit so let's go into our views folder and then in stories and let's create a new file called edit dot HBS and I'm gonna copy what I have in my add HBS because it's very similar and we'll paste that in it's just a edit story and I just want to see if it renders so I'm gonna save this and go back and I'm gonna click on one of these and we see our edit page now obviously we want to have our data our story that we reading which is this one here this is this is the ID we want that to show up in the form so back in our edit let's go let's start with the title right here so this is pretty easy we can just add a value and inside here we should be able to take our story and call dot title and if we go back and reload now we get the story title for the the select is gonna be a little difficult with handlebars if we want to have a pre-selected select its we have to we're gonna have to add a helper but let's do the body if we go down here we should be able to just write in the text area we should be able to just put in story dot body like that so now if I reload there we go good so for the select this is this is gonna be a little difficult we have to well not difficult here but in the helper so we're actually going to wrap this we can create helpers that wrap things so we're gonna wrap the options in a hash select and we want to put we want to pass in the story status as an argument right and then we want to end this after the options like that so so this this is nothing right now but we can we can turn it into something by going into our HBS file and I'm actually going to just grab this real quick so let me grab this and go right under the Edit icon and paste this in and I'll be honest with you this is something that I just found I think on Stack Overflow because I could not figure out how to have something selected in handlebars that was you know because obviously if it's public we want public to show if it's private we want that to show and this is I think this is a snippet that I found to be able to do that so it takes in whatever the selected option is and then the options themselves which we actually wrapped here and what its gonna do is set selected on if it's you know if it's public it'll be on the public option if its private it'll be on the private option and it uses regular expression to do that so let's save this and it's not gonna work just yet because we have to register this just like we would any other helper so in our app J s which is Jesus this is a lot of files you want to go up here and let's add in select and put that and here select and that should now select the correct option so if I reload I mean it's it's public but I think all of our stories have been public so let's see what we can't change it yet because this doesn't work yet but when we do add the functionality to edit this story well you know what let's create a new story so I actually want to go to dashboard and create a new one so this is say gen story three and let's set this to private this is my private story okay so we'll save that so this one's private and we'll go back to public stories not public stories because it's not gonna show here which is good it's pretty private so it shouldn't show here what I want to do is edit which I don't have the edit button so I'm gonna have to just manually and these should be links those should be links for the dashboard okay so this didn't go around that told you this wouldn't be polished so if we click on story three obviously we can't get that but let's say stories / edit / and then the ID okay so now you can see private is selected by default so that's that's all I wanted to check now we want to have this form actually make a put request because when we make updates on a server updates to a database we want to make put requests now we can't do that by default right we can't go to our edit form and say method put our method delete we can't do that it can be get or post so that's where that method override package comes in so I'm gonna try to minimize the files I have open because I know this this must be confusing so the way that we use method override let's go to our app j/s because there's some middleware and stuff that we have to actually add in so first thing of course we want to bring it in I'll just bring it in right here so let's say Const method override and we want to set this to require method override now I'm gonna go to the docks for this okay cuz we need to see what we need to add as far as middleware and there's a few ways to use this I think yeah what we want to do is this right here the way that this is gonna work is in our form we'll have our standard method equals post but then we should be able to add a hidden input with the request we have or the method we actually want to use which is going to be put in this case we'll also do a delete as well so this piece of middleware right here what should allow us to do that and you can see the example in the form there's a hidden input with an underscore method as the name and then the value can be put or delete or whatever so I'm gonna grab this yeah let's grab this and let's put it I'm gonna put it near the top to paste that in or just say method override okay so it's just gonna check the request body it's gonna look for this underscore method and it's gonna replace it with whatever we whatever we add whether it's put or delete let's just change this to let I don't like the look of var so look in URL encoded post bodies and delete it so it'll actually delete it and replace it with with put or delete all right so let's save this and let's go to edit our edit HBS and so I mean we're gonna keep just method post but as you can see in the middle where it's gonna delete that I don't know it's not going to delete that it's gonna replace that with whatever we put as a hidden input so we'll go right here actually I guess we can put it right here it doesn't really matter so we'll put an input with the type of hidden and let's give it a name of underscore method and let's give it a value of what we actually want this to be which is a put request now an alternative to this is to have some front-end JavaScript where you actually submit it you know in your front-end you get the data make a request with fetch or Axios or something like that but this is just an alternative so we don't have to add that front-end JavaScript I probably should have said that in the beginning now we need to handle this put request that this form is gonna make so we need to go to our routes our story's route so this shows the edit page but we need a route to actually process the edit form so I'm gonna grab this just copying this because it's the shortest and then we'll go down here and let's say update story so this is a it's going to be a put request to stories slash and then the ID okay and then let's change this to just this is gonna be just slashed slash ID but it's gonna be a router dot put it's gonna be a port request all right let's see we're gonna fetch we're gonna check to see if the story is there so let's say let's story equals a weight okay we have to add a sink here so a weight and then we'll take our story model and let's use find by ID pass in the ID which is from the URL so request dot params oops dot ID and then we just want to add dot lean onto this and then we're gonna check for the story or I should say check if there's no story so if no story then let's return Fred's dot render error slash 404 okay and then again we're also gonna check to see if it's the owner I could have just copied this here let me just grab this part so check to see if it's the owner of the story okay so it should equal the story user should be the logged in users ID if it's not it'll redirect if it is then we don't want to render this we want to update the story so we'll set this story variable because we used let up here so we use that story now we're just replacing it or reassigning it to oh wait and then story dot and I'm gonna use find one and update so I'm using this Mongoose method and we want to find by the ID so request dot params dot ID and then as a second argument after the object we're gonna put in the request dot body cuz that's the data we want to replace it with and I'm also gonna add some options as a third argument one is new and I'm gonna set that to true so it'll just create a new one if it doesn't exist and then run validators run validators I'm also gonna set to true which means it'll just check out make sure that the Mongoose fields are valid and then finally we want to redirect so after that's done let's res dot redirect to the dashboard okay so we'll save that we'll try it out I'm gonna just reload this and I'll call this instead of Gen Story three I'll call it updated Story three we'll change it to public save cannot put stories okay so let's see what that just did so it did put stories but we got a 404 and I think I know why yes because the action should not be just slash stories the action should be stories slash ID okay so we need to actually put the story ID in here so slash and this will be story dot underscore ID okay we'll try it again we just submitted it to the wrong place updated story public save okay so updated story three and it's set to public so it did in fact update okay so we can create we can get all the stories we can get all the user stories for the dashboard and we can update I think now what I want to do so we still have to do the delete and we still have to do the single story if to be able to view that let's let's go ahead and do the delete since we're already dealing with the method override because we're gonna have to do that as well for delete so in the dashboard I want to have an edit icon that simply goes to the edit page which we already have and then a delete icon which is actually going to be a button it's actually going to be a form with a button that uses method override to delete so let's do that let's go to let's close this up and go to our dashboard and we have this this empty column right here and this is where we want to put those buttons so let's do that we're gonna have the edit and the delete the edit will just be a link cuz again it's just gonna go to a page so let's give it a class of BTN BTN float and let's see we want this to go to slash stories slash edit / underscore ID because remember we're looping through the stories so we're just getting that particular stories ID and then let's put inside the a tag here at an icon so FA s FA - edit and that's it so if we save that we take a look now we have an edit icon if I click it it brings us to the Edit Page if I cancel brings us back to the dashboard alright good so for the delete we're gonna go right underneath the link and we're gonna use a form now the action is gonna be slash same as the edit it's gonna be stories and then the ID so underscore ID and we're gonna put in method equals post although it's not really gonna be a post because using the override and then I'm just going to give this an ID of delete form and I think that should do it so inside the form here we're gonna have the same type of hidden input that we did with the put so we want to give this a name of method underscore method and a value of the actual method we want which is delete and then of course we need a button so let's say button BTN red and we want to make sure that this is a submit button so we'll give it a type okay so we're just submitting a form and then inside here let's put a trash icon so we'll do FA s FA - trash and let's see if that shows up okay now it shows up however I want these to be side-by-side so yeah and oh yeah the button float so I gave this a button flow class that's not an actual like materialized class or anything I'm gonna add just a little bit of custom style for that so let's say BTN float and we'll just do this the easy way float left it'll set a margin right 10 pixels and if we reload there we go so it looks good so if I click delete here obviously we don't have a route for this but you can see it is in fact making a delete request - story slash ID so let's now handle that so we'll go back to our routes stories J s and from here we want to create a delete route so I'm gonna grab this so the soil delete story delete story /i d delete ID okay so the way that we want to handle this is pretty simple we're just gonna try catch so we want a weight story I didn't do a try-catch with this that I will have to add that after for the put so we want to make sure this is a sink and we want to await not wait a weight story and then I'm gonna use the remove method so I'm going to remove the story that has the ID that matches the request parameter and then I'm gonna redirect okay once it's removed will redirect to the dashboard and if something goes wrong here we'll just console error error let's return res dot render err 500 and I'm just gonna copy this because I want to wrap this here in a try-catch as well actually I'll just grab all that they set in and then put that in there all right did I not do it here either oh this is just the show edit page we should still we should still wrap that grab this sorry guys I know this is a lot of code and it could be a little confusing we don't need this long I don't know why that's there I know it can be a little confusing but you have the repository so if you need to see the code and you don't want to keep pausing the video you can just check it out there so we should be able to submit our delete to this route here and it should remove that story so let's try that out okay so I'll just I'm gonna delete this one here updated Story 3 ok so it goes away good reloads not there cool so we're now able to delete if you want to create an alert partial or something to show when you delete or add or whatever you can do that but this video is way way too long so I'm not gonna do any extra so we can delete now I think I think the last thing we need to do is just the full story right well if we click read more I want to be able to see the full story and I think then we're done so let's add that so for the route let's see the route is gonna be it's gonna be a get request to the story slash ID so I actually want to put this up here let's put it right under they get all stories so I'll copy this and let's go under or show all stories we'll go under that it's a show single story it's gonna be a get request two stories with the ID so get ID okay now we need to fetch the story from the database so let's say let story equals let's make this a sink so story let's whose find by D and request dot trims start ID and I'm also gonna populate with the user data so I'm gonna use populate user and also lien so I'll get the story and then let's check see if the story isn't there okay if the story is not there then we're gonna return res don't render error slash 404 and then we can render the template which we haven't created yet but we will in a second so let's say render and it's gonna be in stories and it's gonna be called show and then we just need to pass in an object with the story that we fetched from the database so we can display the data and down here we'll just do a console error and then let's do a res render I'm actually gonna render 404 cuz chances are if this is an error it's just because it can't find it so let's save that and then let's create the template or the view so in views stories I'm gonna add a new file called show HBS so in this show I guess you know I'm just gonna paste this in and if you guys want to copy and paste from the repository feel free I know you guys are probably tired especially if you've gone through this whole thing in one sitting congratulations if you have so we have just some materialized classes we have an h3 with the title and then we're also using the Edit icon helper here passing in the story user the user itself we don't have to do you know the dot dot slash because we're not inside of an each loop this is just the global user which is the logged in user the story ID and then false for the floating because this is not a floating icon so here we just have card content or formatting the date that's actually not the formatting I want though well we did in dashboard so I'm gonna grab this and put that here okay then we have the story body notice it's wrapped in three because we want to parse the HTML and we don't want to truncate it or anything this is the full story down here we have a card basically this is going to be on the side it's going to have the users display name the user's image and then just oh yeah we have to do that too because we want to be able to link to a page that has all of that users stories and it'll just say more from and then the store the users first name now I'm gonna save this this image small is not an actual class the reason I'm using this is because the image is way too big by default so I just want to make it a little smaller so we're gonna go to our custom CSS and say image - small set a width of 180 pixels okay that should be all we need for any custom CSS we'll close that let's make sure this is saved and our route should render this template so let's check it out so I'm gonna click read more and there we go let's look at the long one just this one okay so we get Story 1 see the icon it's it's not floating it's just uh just the icon just sitting there and then we have the date the paragraphs the username the Google whatever they have for the Google image and then this more link which doesn't work just yet so that should be the last thing we have to do but yeah so we can see all the stories we should probably put a back button or something but it's I guess it's fine and you guys can add on to this if you want and feel free to even make pull requests but yeah the last thing I want to do is I want to be able to click on this did I make this a link yes so this will also go to the user's stories so we have to create this route and deal with that so let's handle it now so it's route stories so we want the user story so I'm gonna should I use for this show single story I'll just copy the first one this is a lot of code for one YouTube video so here this is gonna be the user stories it's gonna be a get request a story slash user slash and then it's gonna be the user ID okay so here we're gonna say get its already has the stories so we want slash user and then he user user can't even spell getting so tired so user ID and it'll get rid of that ok so inside the try here let's fetch our stories keep forgetting a sink so wate story dot find and we want to get only where the user is equal to the request dot grams dot user ID okay and we also only want the status of public because we don't want any of the private stories to show up here and then let's add populate user and lien okay and then what we want to do is render the we're just gonna render these two stories index so it's the same template that the public stories used although it will only show the stories that belong to that that user that's in the URL so let's say res dot render stories slash index and we're passing in stories which again is the the user stories and then here we'll just a console error and Perez render all right so let's reload this so if I click one of these one of these names it'll show me just the stories from that user ok if I click just Jen it'll show her and if I go to the read more and I click right here it'll show only her I don't know what I think that translate things popping up because of this but yeah so we can see only a specific users set of stories ok so I think that's that's pretty much it so I hope you enjoyed this and I hope you learned a lot from it I mean we did a lot of different things that you can incorporate into other applications so hopefully that helps you out and if you want to add on to this if you want to change it into an API so that you can use react or view or angular on the front end you can do that as well I encourage that I always encourage to take what you learn in the tutorial and either build something else from it or make it better add on to it and so on but that's it guys thanks for watching I am really tired I did this all straight through so yeah I'm a little beat but thanks for watching and I'll see you next time
Info
Channel: Traversy Media
Views: 313,315
Rating: 4.9731493 out of 5
Keywords: Node, Node.js, Node.js full app, Node.js express, Express.js, node mongodb
Id: SBvmnHTQIPY
Channel Id: undefined
Length: 148min 43sec (8923 seconds)
Published: Fri Jun 19 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.