Afrikaans
Akan
Albanian
Amharic
Armenian
Azerbaijani
Basque
Belarusian
Bemba
Bengali
Bihari
Bosnian
Breton
Bulgarian
Cambodian
Catalan
Cebuano
Cherokee
Chichewa
Chinese (Simplified)
Chinese (Traditional)
Corsican
Croatian
Czech
Danish
Dutch
English
Esperanto
Estonian
Ewe
Faroese
Filipino
Finnish
French
Frisian
Ga
Galician
Georgian
German
Greek
Guarani
Gujarati
Haitian Creole
Hausa
Hawaiian
Hebrew
Hindi
Hmong
Hungarian
Icelandic
Igbo
Indonesian
Interlingua
Irish
Italian
Japanese
Javanese
Kannada
Kazakh
Kinyarwanda
Kirundi
Kongo
Korean
Krio (Sierra Leone)
Kurdish
Kurdish (Soranî)
Kyrgyz
Laothian
Latin
Latvian
Lingala
Lithuanian
Lozi
Luganda
Luo
Luxembourgish
Macedonian
Malagasy
Malay
Malayalam
Maltese
Maori
Marathi
Mauritian Creole
Moldavian
Mongolian
Myanmar (Burmese)
Montenegrin
Nepali
Nigerian Pidgin
Northern Sotho
Norwegian
Norwegian (Nynorsk)
Occitan
Oriya
Oromo
Pashto
Persian
Polish
Portuguese (Brazil)
Portuguese (Portugal)
Punjabi
Quechua
Romanian
Romansh
Runyakitara
Russian
Samoan
Scots Gaelic
Serbian
Serbo-Croatian
Sesotho
Setswana
Seychellois Creole
Shona
Sindhi
Sinhalese
Slovak
Slovenian
Somali
Spanish
Spanish (Latin American)
Sundanese
Swahili
Swedish
Tajik
Tamil
Tatar
Telugu
Thai
Tigrinya
Tonga
Tshiluba
Tumbuka
Turkish
Turkmen
Twi
Uighur
Ukrainian
Urdu
Uzbek
Vietnamese
Welsh
Wolof
Xhosa
Yiddish
Yoruba
Zulu
[Music]
hello and welcome to seven hours of
node.js tutorials and instruction this
video is made up of 15 tutorials for
learning node.js that build upon each
other much like the chapters of a book
you will build projects and learn the
fundamentals of node.js the express js
framework and mongodb
before beginning i recommend that you
already know the basics of javascript
this tutorial is for node.js beginners
but not absolute beginners in javascript
throughout the lessons in this video i
will mention links being available in
the description below i've compiled all
of these links into one github resource
that you will find in the description hi
i'm dave gray and i'm the creator of
these node.js tutorials you can
subscribe to my youtube channel for more
tutorials like this one you can also
follow me on twitter and if you're
feeling generous you can even buy me a
cup of coffee let's get started learning
node.js with chapter one
here we can see node.js is a javascript
runtime it's not a new language it's not
a different language it's a javascript
runtime and it's built on chrome's v8
javascript engine it's not a framework
or a library either but it does run on
the server and that's what makes it
different we're used to javascript
running in the browser where the html
and css is and that's all on the front
end but no js is on the back end
so you'll want to go to
nodejs.org if you haven't already
download note downloaded node.js
and you'll want to download the
recommended for most users version is
fine or if you want to get the one with
the latest features either way
download node.js
and you probably already have it or
familiar with it but if you're not i
just want to mention i will be using
visual studio code and you can download
that code editor you can also use
others that you're familiar with however
my examples will be with visual studio
code and you can get that for free at
code.visualstudio.com
okay with nodejs and visual studio code
both downloaded and installed let's get
started okay i've got visual studio code
open and you can see i have created a
new folder just for this tutorial called
zero one tut you can create a folder and
name it whatever you want to but open
that folder up in visual studio code and
then go ahead and create a new file
named server.js
now with that said i want to mention you
should already know some html css and
javascript before this tutorial series
possibly maybe some experience with
other libraries and frameworks like
react or vue but that's not necessary
but you will need the basics of html css
and javascript so while it is a
nodejs beginners tutorial series it's
not an absolute beginners for web devs
if you haven't already learned html css
and javascript you should probably start
there before attempting this series
now with that said we need to focus on
how no js differs from vanilla js just
plain old javascript first of all and
what will be the running theme
throughout the differences is that node
runs on a server
not in the browser and that is a big
difference so you're working on the back
end not the front end we're not relating
to the browser anymore
and because of that the console is now
in the terminal window it's not in
the console window of devtools in the
browser because we're not using the
browser at all let's open a terminal
window by going to the terminal menu and
choosing new terminal in vs code and
with the new terminal window open
we will be able to run node right inside
the terminal so if i type node we're now
running the console
directly in the terminal so if we can
add in any actual javascript that would
be
an expression such as two plus two and
we get four in return
so this is just like the dev tools
console
except we're now using the terminal for
node and with that said we can also exit
by pressing ctrl c and then it will tell
us to press ctrl c one more time and now
we've exited node but what if we put a
console statement in our file so if i
just put in
condyle console there we go console.log
and i put in your standard
hello world and save
we can also run
any javascript file from the terminal
with node we'll just type node and then
the name of the file we don't need to
put the js extension after i'll press
enter
and now we get hello world in the
terminal another difference between node
and vanilla javascript is that there is
a global object instead of a window
object the window object once again
referred to the browser where we could
do window dot inner height
and different properties like that the
global object is much smaller but it
does have some of the same properties we
were used to seeing in the window object
so what i'm going to do
is just say console.log
and i'll put
global in here because global is the
keyword for the global object
and i'll save
and now we can run our file once again
by typing node server in the terminal
and it logged the full global object
i'll expand the terminal window up and
scroll just so we can see this object
here so we have our hello world again
then here is the global object and you
can see there's a clear interval clear
timeout set interval set timeout those
are things that we already had in
vanilla javascript that we're used to
and it's a much smaller object than the
window object if you were to go into a
browser and to log that but that is
another difference in node.js a fourth
difference in node.js
is that there are common core modules
that we will explore and vanilla
javascript doesn't really have those
these are modules that relate to the
operating system the file system
and other things that we can do on the
server and to import those common core
modules as well as any other modules
we use common js modules common js
imports instead of es6 imports and now
there is some work being done
to use the same syntax that we typically
use in vanilla javascript that is such
as import whatever name from whatever
file
and that is how we do it in es6 however
common js uses a require statement so
let's go ahead and
import something and we can learn how to
do that with
let's call this os and set
this equal to
require dot
os
and that's pretty much all there is to
it it's just a different syntax than
we're used to seeing with the import and
now that we've imported the os let's go
ahead and get some information about the
os from it so we can say os dot
type and we'll go ahead and log that and
i'm going to copy this down a couple of
times and change type to a couple of
other values we can get about the os
such as
version
and home directory and that is
abbreviated with home dir you can see
that i'll comment out the global object
log because it takes up a little more
space let's go ahead and save this and
once again
call node server in the terminal
and here we get our hello world and then
you can see os type delivers
windows nt
os version is windows 10 pro
and then os home directory shows my home
directory in users
dave gray there are a couple of other
values that we always have access to in
node
and let me type the first one out it's
der name for directory name and it
starts with two underscores and that
will always give us the directory name
in node
and then the other one
is two underscores
and file name for the file name we're
using
here i can see i'm being inconsistent on
the semicolon so if anything i'll just
remove the one i typed let's go ahead
and save that and once again call node
server in the terminal and here's what
we get after the home directory that was
delivered from the os common core module
in our console log statement right here
you can see directory name
the value that we have we don't even
have to import a module to get that is
this full length here to my 01
directory and then you can see file name
gives that same full path but also
includes the file name at the end
there's another common core module that
we can import and i'll do that right now
and we'll call it path because
it is called path as well so
there is path with the common js import
and now we can use
file name within path so let me give
some examples of that to get more values
here we'll log the path
directory name and pass in
the file name and we'll see that it is
very similar to what we just got by
using the directory name here however if
we're using path that might come in
handy as well
there's also path dot base name
and we'll also pass in the file name
there
and i'll copy this down one more time
and there we'll just change base name
and this will be
extension name and we'll go ahead and
save that
now let's run the file one more time
with node in the terminal
and see what we got in return so after
our directory name and file name here if
i drag this up just a little we can see
it all
so we have the directory
that's the value before we use the path
import and here is the server
file name before we use the path
now the final three that we used with
the path import
we got the same as we did above for the
directory so using the path directory
name is the same as using this value in
this instance
and then the base name was just
server js so that allowed us to just
pull the file name out instead of having
everything included
as the file name value does up here on
line 22
and then the extension name gave us just
the extension of the file the dot js
what i've found to be even more useful
than any one of those individual values
is to oh i had my fingers on the wrong
keys console.log
path
dot
parse
and then pass in the file name
and we'll see what we get in return
once we call this one more time in the
terminal node server and here we get an
object with all of these values so we
have
the root
the directory
the base
the extension
and the file name itself so we can
really pull all of that together in any
way we want to if we just go ahead and
parse and then get each individual value
as well now besides the common core
modules we can also pull in packages
that other developers have created and
some of you may be familiar with node
package manager already and i will cover
that in some detail in the near future
but we can also create our own modules
and so we have a couple of imports here
we'll make room to import one more but
first in the file tree let's create
another file and i'll just call this
math.js inside of mathjs i'm going to
create some functions i'll call the
first one add and set it equal to
a
anonymous function it's an arrow
function really
and from there it'll just be a plus b
which would return the sum of the two
parameters a and b now i'm going to copy
this down three times and we can just
change these so
here we'll have
subtract they'll all have the two
parameters a and b we can also have
multiply
and we can have divide
so instead of a plus b of course it
would be a minus b
a times b
and a divided by b
right now though while we have these
four functions we have not exported them
in any way so we can have a statement at
the bottom that says
module dot exports
and then we can set that equal to an
object and we can pass in the names of
all the functions
there is another way to do this that
i'll show you right afterwards
but first let's attempt this way which
is a common way to see this so we're
exporting all of the functions that we
defined to import these back in the
server we need to come over here
and let's just define math and we'll set
this equal to
require
and now since it's not a common core
module we can't just say math we have to
say dot slash
and now math is available to us there
but we don't need the js extension so we
can save that and then let's go ahead
and comment out all of these console
logs
and we'll add the console log for the
math underneath here so console.log and
then we could say
math.ad and we could pass in two
variables two and three
and let's go ahead and call that below
with our node server
and we got hello world of course it's
still in there and then we got the total
five
so there is math being called however
we don't need to call it as an object
where we use dot notation we could
destructure right here so instead of
math we could destructure
and just say add
and remove the math dot and save and
this should work in the same way so if
we go ahead and call the server again
and we got five once again with our
hello world now we could go ahead and
destructure and pull in the rest
of the functions as well
subtract
multiply
and divide
we have all those functions available to
us now so if we go ahead and copy down
and we'll change each one to
subtract
multiply
and divide
we can
save go ahead and call the file once
again and here are the values looks like
i'll need to move this just a little bit
to see all the values but we've got five
and then two minus three is minus one
two times three is six and two divided
by 3 is essentially 2
3.66
and on now before we're finished with
the custom module let's go back to math
and i'll show you one other way this
could be done instead of combining all
of these at the end
there we go if i'll comment out this
export here
instead of the const here with each
definition
i'll just select all of those
we could change this to
exports dot
and then it's essentially adding each of
these functions to the export so
exports.add is equal to this anonymous
function and
exports.subtract is equal to this
anonymous function and so on but this
will work in the same way so once again
if we go ahead and call
the server file from node we've still
exported all of those functions
and we actually deconstructed them over
here to pull them in so you can also
export
in that way instead of using module
exports at the bottom okay with that
said we have covered quite a bit but
there's one more difference to point out
that we will see down the road and i'll
just put it here under number five and
that is nodejs is missing some of the
apis that are available to vanilla
javascript and one notable one is fetch
but of course we can always pull in
packages into node and there is a large
amount of packages available through
npm4 node so we won't miss it that much
in the next tutorial we'll be working
with files as far as reading writing
creating updating deleting all of the
things that we can do with files and
we'll be using the file system common
core module for that
today we're going to look at the file
system common core module for node.js it
allows us to create read update and
delete files and work with directories
on the server and that's because node.js
is a javascript runtime that actually
runs on the server instead of in the
browser now i'm going to click docs at
nodejs.org
because nodejs.org is the source of
truth for information about node much
like mdn would be the source of truth
for information
about vanilla javascript as well as html
and css currently the stable version
that i have installed is 14.17.5
as you view this tutorial the versions
that are stable may change so go ahead
and click on whichever version you have
and now you can see we have node.js
documentation today we're looking at the
file system module so i can click that
right here
and there are many things you can do
with the file system with directories
and different files so amongst all of
those things what i usually do is press
control f i'm on windows and then i type
what i'm looking for and we're going to
start out by
reading files so when i type read file i
can see the fs dot read file and it
shows the structure here for what i want
to call
and then you can click it and get
details about that i just wanted to show
you how to find the different
documentation on node.js and now let's
get started in visual studio code as we
work with the fs the file system common
core module i've got visual studio code
open with an empty index.js file i've
also got a files directory here in the
file tree and you can see a couple of
starter text files a lorem text file
that has a lot of text in it and a
starter text file that just says hi my
name is dave we're going to start by
importing the fs common core module here
and we use common js imports for that
and so it's just require fs
after that let's go ahead and read
that starter file so we need to specify
the file
and it's in files and then it's at
starter
dot text
from there
we have a callback function
that has error and data that we read
and so we'll say if air we need to throw
the error
and otherwise let's log the data to the
console
now if you remember the console in
node.js
is in the terminal so i'm going to press
control and backtick to open a terminal
window you could also do that from the
terminal menu
now here to run our little node file in
index we just type node and then index
we don't need the js and let's go ahead
and take a look at this data from
starter
and notice after we read the data
it's presented as buffer data so that's
what we get right here if we want to be
able to read the data we can put a to
string method afterwards i'll save
and i'll go ahead and call this again
and now we can see hi my name is dave in
the console
but instead of two string we can also
put in a parameter here that says
the encoding
or defines the encoding so we'll say
utf-8
before the callback as a perimeter and
now let's go ahead and run it again node
index in the terminal and once again we
get hi my name is dave now notice the
throw error here according to the node
documentation if we get an uncaught
exception we need to go ahead and catch
that and i'm just going to paste this in
if we have an uncaught error we should
exit
so
we listen for this uncaught exception
using process now process is one of
those values that node has available to
us we don't need to import it it's
already there so we're listing for an
uncaught exception then we pass in the
error to the callback here
and we're just logging the console
error there was an uncaught error and
then we put the error there and then we
exit the application and this is direct
from the node documentation so let's go
ahead and throw an error on purpose i'll
just look for a file to read that
doesn't exist named hello
and once again we'll call node index and
you can see we get an uncaught error
and here it is there is no such file or
directory now let's change this back to
starter so we can keep reading the data
that exists
and i'm going to put a console log
statement underneath
and this is to demonstrate
that
read file and node in general the
functions or methods you'll find from
node will be asynchronous and so we're
logging the data here but we're also
logging hello here
and node has the ability to say i'm
going to process this but let's go ahead
and tackle the rest
of the tasks in the program and when i
finish reading the file i'll get that
data to you if you're familiar with
async a weight this should be a familiar
concept but let's go ahead and save this
and let's see which we get in the
console first hello or the data
and we get hello and then we get hi my
name is dave so node said it would read
the file but it went ahead and processed
this console log statement and it
actually was logged to the console first
and then when node completed reading the
file it logged the data to the console
instead of hard coding the path like we
see here in read file there is a better
way
and let's pull in the path module to do
that so we have path and then we use
require and we're requiring path
and the reason is the slashes sometimes
if you're familiar with different
operating systems sometimes backwards
sometimes forward there can be some
problems not always but there can be
when you hard code file paths like that
but if we use the path module we can
eliminate this problem so let's use path
dot join i typed json there we go
join
and then we need to specify
first the directory name and that's two
underscores first and remember that's a
value that's always available to us in
node
after the directory name
we'll say the files directory to attach
on to concatenate to that directory name
and then we need to say the actual name
of the file itself so now we've got
starter text so if we supply this
instead of hard coding a file name it's
a much better approach let's go ahead
and save and run this one more time
and everything still works as it did now
that we know how to read files and catch
an error let's go ahead and copy the
read file and underneath this console
log statement i'll paste this in and we
will write a file but it's just a little
different i can change
just a little bit of the copy and paste
so instead of read file we've got write
file
and this path here will change the file
name because we're going to write a new
file this will be reply dot text now we
don't have to specify
the utf-8 that is by default now
we will have a callback and the callback
will only have an error we're not
reading data we're writing it
so if we have an error we'll throw the
error in here instead of data we can
just say
operation
complete or more specifically
let's say
write complete
and we can save this and now let's go
ahead and run this code
and we got an error oh and that's
because i forgot to specify exactly what
we're writing to the file i'll just say
nice to meet you
[Music]
and then we have our callback had things
out of order so again path name
content we're going to put in the file
and then the callback
with that saved let's run it
and there we go we've got hello hi my
name is dave and then the right is
complete let's look at the reply file we
created and it says nice to meet you i'm
going to copy all of this right file
operation down one
and
then we can change it to append file
which is updating a file
adding more content to it
we'll go ahead and create a different
file here
and that is to show you that a pin file
will go ahead and create a file as well
if it doesn't exist so let's just call
this test
and we'll say
testing text or something like that here
and other than that it is basically
the same syntax as
right we'll save this and go ahead and
run the code again
and we got write complete and append
complete and then notice due to the
asynchronous
fashion of node.js
the read completed last this time and we
have hi my name is dave after the other
operation is completed in the file tree
we now have our test file as well it has
the testing text in it so append file
worth noting that it will modify an
existing file
and can append content to it but will
also
it will create a file if it doesn't
exist
so due to the asynchronous nature of
nodejs and these different methods we're
calling here from read file write file
and append file if we wanted to modify
the file that we created such as reply
it would be
better to put a pen file i'm going to
cut this and put it inside of the
callback
of the write file operation that way it
would definitely create the file
and then we would be ready to append to
it
instead of wondering if we would
possibly create the file first with
append
and then write over it with write file
so here let's change this to
reply.text and it says nice to meet you
let's give a couple of line breaks
and then say
yes it is
and if we save that we'll go ahead and
delete the test that we're no longer
using
and our reply because we will create it
once again so we've got reply and then
we will modify reply with append and
it's inside of the callback
of write file
call node index in the terminal
hello
write complete
then we got the read operation completed
and then the append completed
so if we look at reply now it says nice
to meet you and yes it is in the order
we expected now as you might imagine if
we wanted to do something to this file
after we added more content and we
wanted to make sure it happened in the
order it needed to such as renaming the
file then that would need to be in the
callback of the append file i'm going to
go ahead and copy a pen file
and then inside of the callback i'm
going to paste it
and change this
append here
to
rename so we can rename the file and
we're grabbing
the reply file
and then we're going to replace this
parameter with
what we're going to name it
and we'll just call this
new reply
and then of course it has a callback
with an error as well and then we can
put
rename complete
now let's go ahead and call all of this
in the terminal with node index and it
does happen in the order we expect we
got write append and rename and you can
see this time the read operation
completed before the others once again
asynchronous so we don't know which will
happen before the next but in this way
we're controlling it because we're
putting the append inside the callback
of the right and we're putting the
rename inside the callback of the append
let's go ahead and look at our new reply
and it does have the content we expected
now if you've worked with javascript for
a while at this point you would be
saying
yes this is nice dave but this is
starting to look like what is called
callback hell and yes it is because
we're
putting one inside the next inside the
next and they're all inside the
callbacks of the other so
we are controlling the flow but at the
same time we're able to avoid this in
vanilla javascript using async await and
now we can look at how to avoid it in
node by doing the same i'm going to
comment all of this code out
and up here we can get rid of the
console log hello as well
and then at the top where we have fs
we'll switch this to fs
promises
and let's attach
dot
promises to our import here so we are
now importing the file system promises
instead of just fs and now i'm going to
create a function and i'm going to call
it file
ops for file operations and this will be
an async function
and then inside this function we can use
try catch
so here we'll have our catch for any
errors
and now inside here i'll just say
console.error
and pass in the air
or we could throw the air but that's
actually what's happening here and we're
catching them so it will not be an
uncaught error and then inside the try
block i'm going to define data
and set that equal to
fs or sorry before that a weight
fs
promises
dot
read file
and now we need to specify our file path
and that's what we have here part of why
i saved that so i could just copy it up
and i'm going to hide the file tree
momentarily so we can see a little more
after that we can still specify
utf-8
and then we don't need the callback here
that has the error and the data because
we're using a weight and we're catching
the error down here
so this should be just fine now now once
we have the data
let's go ahead and log the data once
again
and i didn't remember my semicolon after
that being a little inconsistent okay so
we have file ops i'll delete the read
file operation
and now here
we're just going to call file ops into
action
we'll have node index in the terminal
oh and i didn't save the file yet
there i'll save the file
hopefully we don't have unexpected
results now and node index in the
terminal and we got hi my name is dave
the data that we expected to get let's
add another await in this process of
file operations
we'll say await fs promises and now we
will write a file
and this file is going to be promise
right so we'll have the same type of
path once again which i can just copy
and paste
but we'll just call this
promisewrite and after that we want to
pass in the data
that we just read
and now i'm going to copy down once
again
and after we write a file let's go ahead
and append to the file
and now
in the append area
we'll just say
nice to meet you
oh let's put a couple of line breaks
here
with backslash
lowercase in so two line breaks
and one more copy down
and now let's rename
[Music]
and we're renaming promise right so
we'll just copy this once again
and instead of content now we need to
put
the new name of the file and this will
be
promise
complete
and we can save that so you can see
we're taking the data from the starter
then we're writing a new file called
promise write then we're appending to
the file
and then we are renaming the file and
finally let's go ahead
and copy these two lines once again
and put them right here
and now we'll call this
new data
and here we want to read the new file
which is
promise
complete and then we'll go ahead and log
that data as well so you can see there's
several different operations happening
here we are reading data from a file
then we're writing it to a new file
then we're appending to that new file
then we're renaming that new file then
we're reading the new file and logging
the data from that file so i hope that's
not confusing i just wanted to show all
the operations that we have gone over
and then use them
in an async await fashion so here is
node index
and here was the original content
hi my name is dave but then the new file
has hi my name is dave and nice to meet
you and let's go ahead and look at the
file tree
there it is
and we've got promise complete right
here that has hi my name is dave nice to
meet you let's go back to the index and
let's add one more thing right here i'm
just going to copy this one line
and above it
i'll paste in again and here instead of
write file i'm going to say
unlink
and after that
we don't even need to pass in data
because unlink
is actually a delete so we can delete
this original file
that is called
starter text there it is
and now
that we have it being deleted and i can
get rid of the space here as well so
we're reading the file logging the data
to the console and then deleting that
file then creating the new file
and we can even get rid of promise
complete here again
and we'll get rid of new reply we're not
using that anymore
let's make sure we save the index
and we'll call it into action
once again we got the same in the
console and we have promise complete
over here but no more starter text it's
gone because we deleted it with the
unlink right here and this is also
available without fs promises as the
first examples i showed that were just
fs and then read file write file and so
on unlink is what you would use to
delete a file okay now i'm going to
create a new file over here and i'm just
going to call this
stream.js
now if we have larger files sometimes it
is good to not grab all of the data at
once it could be too much just like
moving a
large pile of sand bucket by bucket or
moving all the water in a swimming pool
bucket by bucket rather than attempting
to grab everything all at once so this
could be more efficient and a little bit
easier on the application if we do this
so let's say cons fs and once again
let's require
fs not fs promises just fs
and here
i'm going to define rs and we're going
to set this equal to fs dot
create
read stream
from here
i'm going to specify our files
slash
lorem.txt once again you could use the
path module would probably be a better
option i'm just doing this quickly so
i'm hard coding that in
and now
putting in the encoding and this is
utf 8 once again so we've created a
readable stream and we've specified the
encoding in the options now let's go
ahead
and specify
a writable stream and this is going to
be ws.com
write stream
[Music]
and now what do we want to
write this to would be the question so
let's call this
from
stream ah let's call it
new dash lorem because it is our lorem
text still that works
so we have new lorem
and really we don't need to specify
anything else here we already specified
what was being read is utf-8 so we're
good there and i did make a mistake here
i needed to make this fs not ws because
this is
getting this from the fs module the
create write stream and that's why it
didn't help me out when i was typing it
okay so once we have that we need to
listen for the data coming in from the
stream so here is our readable stream
and we can say on
now we're listening for the data that's
coming in
and here let's say this is a chunk of
data or a data
chunk if you will
and inside of here we could console log
the chunk or we could just
write to our writable stream and pass in
that data chunk
so let's do that
and i will save
and now let's go ahead and run this so
we'll type node
stream and notice now we have new lorem
as well and this will be a large
file of lorem probably about a thousand
lines yes so it did it very rapidly but
once again a example of a large file
just a
test example we could have much larger
files but in that case when you're
working with a large file this is much
more efficient now speaking of
efficiency i'll go ahead and comment
this out there is even a better way to
do this instead of the listener
just take
the readable stream and use pipe
and then you can pass to the writable
stream this will accomplish the same
thing and piping is more efficient than
this structure with the listener here
i'm going to save this i'm going to
delete the new lorem file
and now let's go ahead and run our
stream once again by typing node stream
in the terminal
and you can see we once again got our
new lorem text file and it has all 1000
lines of lorem ipsum text
okay i'm going to create one more new
file in the file tree and call this
dur.js which is short for directory
once again
let's go ahead and define
fs at the top and require it
and after we do that what i want to do
is create a directory with
fs.mkdir which stands for make directory
and we'll have
let's just call it new we're specifying
what we want to call it right here and
then there's a callback that has an
error
and this is the same as we've seen
previously so if error we could throw
error now i won't go ahead and copy and
paste the uncaught error handling code
in here that i showed you earlier just
to save time but you could use it in the
same way
so if we have an error throw an error
and if not we've created the new
directory so we'll just log
directory
created
and we can save this let's go ahead and
run our code so here we'll type node dur
which is our new file dur
and directory is created and now we can
see here is our new directory now
earlier in the tutorial we threw an
error on purpose because we attempted to
read a file that did not exist
well we can check to see if files and
directories exist or not so we don't get
those errors and we also might want to
check in this regard to say if the
folder if the directory already exists
let's not create it because we don't
want to write over what we already have
so we can use
the exists sync method and let's start
out with if and then we'll say if it
does not and not would be the
exclamation mark
fs dot
exists
is it exist yet two s's exists sync
and then we'll say the same
folder slash or the same directory dot
slash new so if it does not exist
then go ahead and do what's inside of
the if statement
and we'll put that right there let's go
ahead and close that out and close this
out
and save and now let's see if we create
the directory when we run it in the
terminal
and no no directory was created because
it exists
so this says if it does not exist create
it so let's go ahead and delete the
directory
and now let's run this once again
and now it created the directory again
because it did not exist you might find
checking for file existence fairly
useful before you attempt to delete a
file if it exists or not or before you
attempt to even rename a file or copy a
file several things that you want to
make sure the file exists likewise you
can check directories in this regard now
let's go ahead and write some code that
deletes
the new directory okay let's copy this
down
highlight everything
and bring it down here to line 10
and now let's check to say if it does
exist
and this is the folder we're going to
remove the directory and that is
rmdir for remove directory so this would
be a zero sum game here if it does not
exist create it
and if it does exist remove it so now
let's say directory
removed
and
we'll be able to tell by the console but
in the file tree
we should not have a directory when
these operations are complete
and the directory was removed it already
existed so it wasn't created but it was
removed let's go ahead and run it again
and now it was created and removed and
so it's still not in our file tree but
we can see both operations completed
today we're learning about node package
manager which is also commonly known as
npm and when you install nodejs from
nodejs.org which i have open right here
you also install npm however npm also
has its own site and documentation at
npmjs.com so let's take a look at that
and you can see the
npmjs.com site here one little quirk i
really like about
npmjs.com is they give
wrong answers for what npm stands for in
the top left so every time you come back
to the site you'll see something
different here right now it says new
prog mixtape it really stands for node
package manager and npm provides
thousands of packages for us to use in
our applications the difference between
a node common core module and an npm
package is that npm packages are node
modules that are created by third
parties in other words other developers
so let's search for something here like
axios which is a node package
and we see the results axios is at the
top with an exact match and you can see
they're also ranked by popularity
quality and maintenance and you see the
little chart over here to the right so
essentially other developers create
reusable modules and share them through
npm so instead of recreating the wheel
we can search for a module that already
exists and then we can import that
module into our applications now today
i'm going to go over what i consider to
be essential npm knowledge and we're
also going to install some packages that
we'll need in the near future when we
create an event logger i'm going to
start by showing you where the
documentation is at
npmjs.com click on documentation at the
top then it takes us to docs.npmjs.com
now when we're there what we're really
working with
is the cli
and that's what developers typically use
when they're integrating npm into their
projects and their cli commands
configuring npm and using npm there's
quite a few commands i'll just go over
what i consider to be essential what i
use the most and what you really need to
get started but i always want you to
know where to find that documentation
and reading the docs is definitely a big
part of being a developer okay let's
move over to visual studio code i've got
visual studio code open and i have a new
folder for the tutorial and then an
index.js file that is completely empty
right now we're going to start off by
just putting a console log statement and
saying testing
inside of our index.js and we can save
that file now i want to open a terminal
so we can go to the terminal menu or
since i'm in windows i'm just going to
press control and the back tick and it
will open up a terminal
now in the terminal
we can install a node package globally
that we can just call from the command
line without adding it to a specific
project so i'd like to do that first
i'll type npm
and then the word install works but you
can also use the alias i or there's also
an alias ad
i typically use just i
from there i want to install node mon
and this is a great development package
but it's also good to install globally
because you might just want to call it
from the command line
and with nodemon
we want to put dash g now what nodemon
does is it monitors your files and as
you save it automatically restarts the
server so we're not always typing node
and the file name in the terminal it
just kind of does it for us so i'm going
to go ahead and press enter to install
this package globally and then when it
finishes we'll see how it works with our
index.js okay and the package installed
we're ready to try out nodemon so let's
type nodemon instead of node
and it's going to look for the index.js
by default so i don't really need to
type that name but if i had a different
file name like server i would type
server or something like that right now
i'm just going to type nodemon
and it will know to look for the
index.js and it says it is watching the
extensions js mjs and json and it
started our index.js
and here we can see the message testing
so it did go ahead and log to the
console testing now i'm going to change
the file
add an exclamation mark and save and
then you can see it was restarting due
to changes and then it went ahead and
ran the file and we got testing in the
console so that's how nodemon works now
to exit nodemon you can press ctrl c
just like we would exit a node
application that we were running if we
tested it by typing node index now that
you know how to install a package
globally let's go ahead and add a
package to our project but before we do
that we have to initialize npm for our
project i'll type npm and then just like
when we initialize git if you're
familiar with working with git
we type init now it will ask some
questions at the beginning if i want to
skip those questions i'll just put in
the flag hyphen yes to answer yes to all
the defaults but i'll go ahead and not
do that just to show you the questions
it will ask
and then i'll just press enter on each
one to go ahead and accept the defaults
but first it's going to initialize npm
okay it's asking me the package name and
the default would be zero three tut what
i named the folder
and then the version by default is 1.0.0
description you don't have to enter one
entry point by default is
index.js but we could change that to
server.js or something else if we wanted
to
test command nothing to enter there we
don't have a git repository yet no
keywords you could put your name for
author if you want to
and there's the license by default and
then it says is all of this okay and the
default is yes so i'll just press enter
one more time
now if we look over
in the file tree we see a package json
file and this pretty much has all the
information that we answered those
questions to then the package json file
is important because this is what npm
reads to know what packages to install
for your project and that is because
this file will stay with your repository
if we send it to github
but we won't send the packages itself
and that way we don't have to transfer
as much data or store as much data
and then when somebody else clones or
installs this application it can just
read the package json or likewise if we
were to host it and build the
application
at a host and run a build command
then it would install those packages
there without having to transfer them
over from github
so now that we have this let's go ahead
and add a package to our program and i'm
going to say npmi
and then i'll type
date dash fns which stands for date
functions
and press enter and it will install this
package as a dependency which we will
see in our package json file okay that
completed installing and we now see
dependencies listed in our package json
file and these are production
dependencies we see listed here so when
the project would build with a build
command it would include this package
because we would know it would be part
of the overall application that needed
to go into production but we can also
have dev dependencies before we get to
the dev dependencies though let's go
ahead and look at the file tree because
now we have a package dash lock dot json
and we don't want to change anything in
there that's just handled by npm but you
will see it show up in its file tree
we just work with package.json we also
see a node modules folder and this can
get fairly large fairly fast because
there will be a lot of
files and folders in here and that's
because any dependency that we add can
also pull in other dependencies and if
this package needs all of these
dependencies
this gets very large and that's why we
don't want to store that in github so
what we should do
is always add a dot
git
ignore file oh and i didn't press dot i
got a slash there we go dot
get
ignore
and in this get ignore file
the first thing you should add
should be node
underscore
modules
now i'll go ahead and press save and now
if we were to initialize git and save
this to github the node modules folder
would not be included and that's
important because there is a lot of data
in node modules now i'm not going to
delete the node modules folder right now
but if you were to clone another
repository and it had the package json
but did not have the node modules you
would get an error if you tried to run
or tried to start the project so what
you want to do is type npm
install and if you just type npm install
and press enter it's going to read the
package json file and go ahead and
install the node modules you need and
that's important of course when you're
pulling down another repo say from
github maybe you cloned it and
then you want to install the node
modules that are used in that
application okay with the date fns
dependency let's go ahead and use it now
in our index js i'll get rid of this
console log statement that we have here
and at the top we want to go ahead and
import and we're going to destructure
because we'll just import format
and this is using common js again so we
use require and then we'll say date dash
fns
and now that we have that imported we
can use it in the statement so i'll just
put another console log here and i'm
going to paste in the format but then we
can go over it so we're calling the
format function from date fns and it
accepts a new date
and after it accepts a new date we have
to tell it how we want it to format it
and of course you have to read the docs
to know how to format it and i am
pulling in the year
the month
and
and i didn't mean to do that let's undo
that the month and the days
and then i'm putting a tab in and then
i'm putting in the hours minutes and
seconds and so this will be tab
delimited so we'll have two columns
we'll have the date and the time and we
could use this in a log format and that
will be the end goal eventually as we
learn about events but right now let's
just format this date and go ahead and
send it to the console
now remember we're not running nodemon
again yet so let's go ahead and add
nodemon
as a dev package to our project
okay back in package json we're going to
add a dev dependency now easily
i could run nodemon right here from the
terminal as a global dependency because
we'd already installed it but i just
want to use this as an example of
installing a dev dependency so here we
type npmi once again i'll type nodemon
but then i want to give the flag you
could do dash dash save
dash dev
but there is just a shorthand for that
and it's dash with a capital d and this
will save nodemon as a dev dependency so
i'll go ahead and press enter and let
that install all right now with nodemon
installed as a dev dependency you can
now see that in the package json as well
and it lists a dev dependency so let's
talk about scripts and how to run
our application using scripts because
that's what a server would use if we
were to host this somewhere and you
often see a start script a dev script
and a build script if you're familiar
with react or something similar you have
definitely seen some of those before so
let's go ahead and add a start script
and now in the start script
we need to just go ahead and call node
not nodemon but node
and we can say index right here because
that is what we want to run our project
with
but then let's go ahead and we don't
need a test script so let's change this
to dev
and i'll go ahead and replace
the test script now with nodemon
and we'll go ahead
and we can specify index here as well
but remember it defaults to index js
anyway so let's go ahead and save that
now if we're to start the project start
is one of the few words that works
without the word run so we could just
type npm start
and start our project but we don't need
to do that yet we want to use
dev so let's say npm
run dev and press enter and you can see
nodemon started and we've got the date
over here with the format i gave it
which is
year month and day and then there's a
tab spacing and then we have the time
and you can see we're using military
time which would be 0 through 23 hours
and then minutes and seconds now back in
the index if we were to make any other
changes nodemon will continue to watch
this
and go ahead and restart the server so
then if i just put another console log
and said
hello
save the file nodemon restarts and we
should get the new information in the
console as well and that's what we get
with the hello now i want to go ahead
and stop this again because we want to
add another dependency to our project
while you don't have to look at the
package json while you install
dependencies i'm going to go ahead and
do that so we can see the new dependency
added and here i'm going to type npm i
for install once again and this is a
production dependency so we don't need
to put a flag after it it'll go directly
to the dependencies and we're going to
install uuid which allows us to generate
ids that of course are different for
each entry so i'll go ahead and press
enter and now we can see uuid installed
here as a dependency in our package json
also so let's go back to index
and use this package as well so we need
to import it at the top
and here we'll say const and now
this is a little different now with es6
imports we would say
import
uuid as and give the name that we want
to give it now here we need to import a
version which is a specific version it's
v4
but we want to import that as uuid
and then we can say equals require
and then specify uuid here now you may
see this done in different ways as well
so instead of the v4 this way some could
just use it as v4
or you might see
an import and just here have uuid
and let me get rid of that curly brace
there
and then in the code they could possibly
use it as uuid dot
v4
and call it that way as well what we're
going to do is use it as uuid so back
here we want to make sure it is v4 so
i'm going to say import v4 as
uuid
and now we can use this and this will
log
a different
id or generate a different id to the
console and then of course our console
log statement is what will log that so
let's go ahead and save this and now
once again in the terminal i'm going to
type npm run dev to start nodemon
nodemon started up and you can see we've
got the format of the date with the date
and time and then on the next line the
second console statement we got the
result of calling uuid and if we just
make any change basically to the console
it's going to generate
another
uuid that's different than the one we
got before of course so you can see how
it generates a different id each time
and that can be very useful including
with something we would log such as an
event whether it's an error or a request
or anything like that that we might
write to a log file we might want to
give each entry its own id i want to
jump back to chrome really quickly just
to draw your attention to
how we can search for these packages
because you might be wondering how do i
know about these packages well you could
search for id
and see what would come up and remember
they're also ranked by popularity and
we've got nano id short id but we don't
see uuid but somewhere i learned about
it along the way and if you do search
for uuid it will show up here it is the
exact match popularity is huge quality
is good and so then when you go to the
page you can read about the details for
the package you want to use so here's
some good information about it it tells
you how to install it and of course it
quickly tells you how to install it here
shows you where the github repository is
weekly downloads that's a very popular
package right there so this is a good
example of course there's some docs as
well and you can look that up and of
course you can search again at the top
i'll look for date dash fns
many times a package will have its own
website as well that has documentation
and date fns is definitely one of those
that do of course
notice it changed nomadic people
migration at the top for npm
i always like the different uh
definitions it gives npm again node
package manager this says it's like
lowdash for dates lowdash is another
package you could use but this datefns
has its own github repository it also
has its own web page that i believe is
linked here somewhere if i scroll down
maybe we'll see that oh here it is see
datefns.org for the docs so there's all
kinds of information about each package
as you explore that i just wanted to
highlight that again as we work with
these and now let's take another look at
the package json because there is a
little more detail we need to look at
look at the packages we have installed
here under dependencies
or also under dev dependencies and you
can see we get the semantic versioning
numbers
now let's talk about these because this
first number means a major version the
second number means a minor version
and the third number
means a patch
now the carrot we see in front
means go ahead and allow an update to
the minor version and the patch if
needed but do not update a major version
a major version could have breaking
changes to your application so you don't
necessarily want to allow those you can
now if you don't put anything in front
of the number
this is saying specifically this version
and only this version for this project
will work so once again if you put the
carrot that is saying go ahead and
update minor versions or patches but not
a major version
so now if we have what's called a tilde
the little squiggly kind of let me put
it there
this is saying go ahead and update a
patch version but do not update a minor
version so if you see the tilde that
will allow patches only and not minor
versions or major versions
now if we got rid of this all together
let me go ahead and put the carrot back
because i'm going to want to
replace that
and you just saw an
asterisk in here
this would mean go ahead and update
everything all the time use the absolute
latest version every time
that is not too safe although you might
see it this is what you'll typically see
and what is installed by default to your
package json is to have the carrot here
and it's worth noting that when you
install if you specifically want to
install a different version or
any kind of version you can just have
npm install
and then say we would type uuid and then
put the at symbol
and after that say the specific version
you want if you wanted
8.3.1
then this would install that specific
version however if you just say
uuid you're going to probably get the
latest version when you install here but
then it's going to mark it to not update
any major changes because those could
once again cause
a break in your application so that
could be what would be considered a
breaking change i'm going to go ahead
and save the package json now and if
there were updates or if you wanted to
check for updates to say okay is uuid
does it have a new miner version or
something like that if we had it like
this remember it would not install the
major version but if there was a minor
version or a patch you could just type
npm
update and npm will check for any
updates
for your packages and you can see i had
no updates or it would say something
about that as well so there were no
updates there so the only thing that i
typically use or would might need to use
that i haven't shown you on a regular
basis would be uninstall so here we can
type npm
and then you could use
the full uninstall
or you can just use on or you can use rm
for remove so let's just go ahead and
use rm and then we're going to uninstall
nodemon but nodemon is a dev dependency
so you always need to add that specific
flag if it's a dev dependency or if we
wanted to install the global version i
would put the dash g as well now if it
was production you wouldn't have a flag
at all
so let's go ahead and uninstall the dev
dependency node mod
this is worth noting we did uninstall it
so there is no longer
a dev dependency listed it went ahead
and kept the dev dependencies in here
but it's just an empty object right now
however we still have nodemon listed in
our script it's not going to
automatically change a script for you so
that could be an issue if you want to
install something that you have in a
script you need to remember to go
and check for that as well the nodemon
is something i would probably have
installed as a dev dependency so we
could just re-add that but again worth
noting if you uninstall a package it
doesn't remove it from your scripts so
you need to check that as well
today we'll be learning about the events
common core module in node.js and more
specifically how to both emit custom
events and how to respond to those
events when they are admitted
right now we're picking up where we left
off in the last tutorial about npm
modules and if you didn't see that
tutorial you can just catch up by
installing the dependencies you see
right here so for production
dependencies we have the date fns module
and the uuid module you could also refer
to those as packages also as a dev
dependency we have nodemon installed and
then in the scripts of course the start
script is node index but what we would
use in development is the dev script and
that is nodemon index so if we type npm
run dev it will launch nodemill and
nodemon will listen for the changes in
our files and restart the server without
us constantly having to type that so
again if you didn't see the previous
tutorial just update your package json
and
npm installs to this and if you don't
know how to do that please do watch the
previous tutorial okay let's move over
to the index js and you can see we're
already importing format from the date
fns module and then we're also importing
uuid and it's actually
v4 there's different versions in the uid
so we're importing v4 as this is an
alias as uuid so we can call that right
here and you can test these out with
these console log statements that we had
from the previous tutorial but what i
want to do right now is rename this
index file so i'm going to right click
and choose rename and i'm going to name
this
log
events in camelcase.js so this is a
complete different file and i'm going to
make this into a module that we import
into an index.js that we haven't created
yet so we need both of these imports
but we're going to make a logging
function of course logging events is
something that's very useful on a server
let's go ahead and also import the fs
module so
we'll require
fs
and then we're going to want to use
promises
so let's say fs
promises
set this equal to
require
fs
and then dot
promises
and we also need the path module
so here is require
path
and now that we have all of the imports
we need and notice these are the only
two that we really needed to use npm for
because these are all common core
modules here that we imported
now that we have that
i'll get a couple of extra lines let's
go ahead and define our log events
function that we can export
and it's going to be an async function
and it will receive a message parameter
now inside the function
we need to define we'll call it date
time and let's set this equal to
and let's just grab this format that we
have right here and copy this
because that's what we essentially need
i'll use a template literal
and inside here i'm going to paste
the format the new date it's got the tab
and then it will just in the template
literal
and leave it at that and then we're
going to take another definition here
and call it log
item
and we'll really use the date time so
i'll put another template literal
and we could have done this all in one
line but this gets kind of long if we do
that so i wanted to break it up
and so we've got the date time then i'm
going to put another tab and remember
there's a tab in here too so what we're
doing is creating a tab delimited log
file
and now i'm going to use uuid
and call that to get a unique id for
each log event
and then one more tab
and we'll just put in
the message
there we go and now that we have our log
item let's go ahead and log that to the
console so we can see it while we're in
development mode here as well
and now we need a try catch block let's
get to the async await portion of this
function
and we'll catch an error if an error
happens and let's just
log the error to the console if we have
an error writing to the log we won't be
able to write the error to the log so
this is the best thing we could probably
do right there
but in the try block
we can
await fs
promises
and then
let's go ahead and append file if you
remember from our tutorial on working
with files a pin file will also create a
file if it doesn't exist and now we need
to use path and we'll use path join
and two underscores and then we'll use
the directory name value and then let's
put it in a logs folder and after that
let's call this event log
dot text
and then we still need to pass in the
content that we're going to put in the
log right after that parentheses we need
the comma and that would be our log item
that we defined okay now we can save
this much
now i am thinking of something that
could cause an error in the future and
we'll get rid of these console log
statements down here but i want to leave
it like this for now so we can actually
test out getting the error as well or
catching the air
so let's do module.exports
and set this equal to
log events
now we're exporting our log events
function and we'll be able to use it in
the index that we're going to create so
over in the file tree we'll create a new
file called index js
from here let's go ahead and define
log events and set it equal to
require
and now dot slash because this is once
again a custom module not a common core
module or an npm module and so dot slash
log events and now we're ready to work
with the events common core module and
what we want to do is define an event
emitter and let's set this equal to
events the common core module events
after that we need to define a class
so class my emitter
and it's standard to capitalize instead
of use lowercase on the first my there
for a class
and then we want to say extends
event emitter
and then we can just have the empty
curly braces right here this is i know
it looks strange but it's directly from
the docs as well
from here we can
initialize
the object that we're going to create so
we want to create my emitter but now
oops
this needs to be a lowercase m to start
out so don't let it change on you or or
select the option that would select the
previous my emitter
and now that we've got my emitter
defined let's set it equal to a new
object that is
my emitter right here so we've
initialized the object i know that's a
little confusing
but that's what it looks like in the
dock so i just stuck with that
and then we've got
add a listener
before
the log event
so then we can say my emitter
dot on and that is how we listen for an
event and i'm just going to call this a
log event
we could be listening for any event we
want to this is the first parameter of
listening for an event so we just say
which event we're listening for
and then we can call this anonymous
function to pass in the parameter
message
and now inside this anonymous function
we'll call log events
and once again send that message that
was passed in
so now that we are listening for the log
event
we need to go ahead and omit the event
to test this out now i want to set a
timeout
which will hopefully
let us understand how everything is
processed a little bit better but we're
just going to
emit the event
you don't have to have a timeout to do
this i just wanted to put a little delay
in there so once again we use my emitter
dot omit now not on on is listening so
emit is emitting the event now we'll
emit the log event
and then let's go ahead and send our
message i'll just say log event
emitted
that looks good now let's put a delay in
here with the timeout and i'll put
two seconds that may be too long it may
not be long enough to see the difference
just depends how long it takes nodemon
to restart the server
so now that we've got that saved and
this is our index let's go ahead and
open a terminal you can do that from the
terminal menu or i do control backtick
i'm in windows
and now i'm going to type npm run dev to
launch nodemon and our index js
and we get an error so let's see what we
got i did expect to get one
notice we did log to the console so
here's what we would have written
to a log file
we've got the date we've got the time
we've got the unique id and we've got
our message log event admitted so that
all worked out as expected but we have
an error and let's see what it says here
console error is not a function well i
should have typed console log here let
me go back and fix that
we definitely have another error that i
was expecting to see so let's drop this
back down and of course nodemon should
restart after i change this so instead
of console error let's put console log
we might have to restart nodemon let's
see yep now it restarted and we can see
the message that i was expecting to get
so here once again we got the log file
and then we got no such file or
directory
and that is because append file will
create the file if it doesn't exist but
it won't create the directory and we
didn't have a logs directory yet so we
need to modify our function because if
you think about this when you install
some software
there may not be all of the directories
created that you need so let's modify
this try block to account for not having
that logs directory to begin with if you
notice we haven't used the fs module yet
we had used fs promises so in the try
block let's say if
and then we'll say
not if it not exist is what we're going
for here so fs dot
exists
sync
and then we want
path path.join
two underscores and directory name and
then we're just looking for the
directory so if this does not exist
essentially is what we're saying
then as you might guess we want to
create that so we'll say await and now
use fs promises
oh and then we need dot
mkdir which is make directory
and we can say path.join
once again
two underscores in the directory name
and create
the logs directory please so now
if the logs directory does not exist it
will create the directory and then it
will either create or append to the
event log file so let's make this change
and now if we look back
we can see we got no error on this
latest execution once again everything
wrote to the console and now i'll bring
this down
and if we look over here we have a logs
folder
and we have an event log
and there it is we wrote to the event
log
now let's go ahead and run
the program again we'll need to make a
change of some sort to do that
and then we can check the event log so
let's just make a quick change here by
putting in a
console.log or no you know what we don't
need to do anything like that we could
even just put in a comment
say testing
save
and we should be able to see some
changes once again if we go back to the
event log oh we've got another problem
after our message
we didn't put a line break
and so then it just wrote the next log
on the same line so we need to put in a
line break after that as well so let's
go to our log events
and where we define our log item at the
end of message then
let's put a line break with a slash in
and now this should make a difference
let's check the event log again
and maybe it made a difference
i'll tell you what let's try this i'll
just delete this all together
now the file doesn't exist
i'll get rid of the testing
save
write the file let's look at the file
there it is
now
back here once again i will undo
put the testing back and save
and let's look at the event log there it
is we've got another log event emitted
on a new line and everything is lining
up as we expect
delete testing again and save
and there it wrote a third line to the
log so this is working as expected so
here is a abstracted
log events function that you could use
you could accept more than one uh
parameter as well
and if there was a second parameter here
when we would be in
the index.js when we admit this we would
just need to put the next parameter
right here
so there can be more than one parameter
as well and this is how
you set up an emitter to not only listen
for
but to emit events now there might be
all sorts of actions that you want to
emit events for when we create a web
server we're wanting to emit events to
show what requests came in
and log all of those so we have some
detail of the activity for our web
server and that's what we'll be building
next a web server with node.js
today we will be building a web server
with node.js no framework will be
utilized and this will help us learn
more foundational knowledge about nodejs
and then in future tutorials i will
introduce the express js framework we're
picking up right where we left off from
the last tutorial about the nodejs
events module so let's go over some
quick changes and file additions and i
will be providing a link to a github
repository in the description below okay
in the package json you can see i have
changed the name to o5 tut for the fifth
tutorial in this series you can change
that whatever you want
now i've also got
server.js instead of index.js as the
main file and i have changed the scripts
accordingly so it says node server and
nodemon server instead of node index and
nodemon index other than that we still
have the same dependencies as the last
tutorial so date fns and uuid are here
and the dev dependency of nodemon is
here and those are the few changes for
the package json of course i have
renamed the index file to server.js you
can see everything else is currently the
same here
in the server.js file that was
previously the index.js file and we will
come back to that i want to delete the
event log file that is currently in the
logs folder you can leave the logs
folder if you want to if not it will be
recreated and that's because we still
have the same log events js from the
last tutorial here as well
and we might make a change to that but i
won't do that now either we'll come back
to the whole events topic
what i do want to show you are the other
folders and files that i've added and
these are simply to be served by the
server so we can make sure it's working
i've got a style dot css file
in the data folder i've got a data.json
and a data.txt
and you can just put in whatever
information you really want to and style
the page or web pages however you want
to i've got one image in here again you
can download the repository and get
exactly what i have or you can use your
own and then in the views folder i've
got a 404.html
an index.html and a new dash page.html
and i also have a subdirectory named
subdir
and in this subdirectory is another
index.html that will be the default page
for this subdirectory route and so we're
going to serve all these different file
types or content types from the server
today now let's make some changes and
additions to the server.js file i'm
going to eliminate some of these blank
lines and then when we created a class
my emitter that extended the event
emitter in the last tutorial it got kind
of weird when we initialized our object
and it was also named my emitter so i
just want to name the class emitter and
this is just kind of for my peace of
mind after that we can get rid of that
extra space too we will leave all of
this here i'm going to get rid of the
comments
about the listener i'll also get rid of
the timeout that we added because that
will not be needed and i'm going to
comment out
both of these right now
and then we'll come back to those later
on when we need them
give a little space in between now let's
go ahead and import the common core
modules that we'll need for this file at
the top and the first one we need is
http
and so that's one we haven't covered yet
that we will today
and we just require http
after that we need path
and we'll require path
and we'll also need
fs for the file system so we'll require
that
expo require
and we're also going to need fs
promises so that equals
require
fs but then at the end dot
promises and those are the common core
modules we're going to need and now we
need to define a port for our web server
so not only will it have the address of
localhost because it will just be a
development server on our local machine
but we need to say what port it will be
on and so now we'll just define port in
all caps definitely a constant here
and we should say process.enb
dot port because if we were to host this
somewhere it would use this information
and then we can say or and i'll say 3500
today i've seen different ports used and
so today we'll be using port 3500 if we
were to host this somewhere
it would have a different value here now
that we have defined the port let's go
ahead and create
the minimal server we can do that by
defining
server and let's set it equal to http
dot
create server
and now that gets a request that we'll
just call req
and a response
in a function
and now we have our request and response
headed into the server and we won't
really do anything here at first except
let's log
the request
dot url
and let's log the request
dot method
we're not quite ready to launch our
server yet because it still needs to
listen
for requests so we say server dot listen
and this should always be at the end of
your server.js file
and we give the port value
and then we have an anonymous function
and here we'll just say console.log and
inside this
console we'll use a template literal
we'll say server
running on port
and then let's once again give the
port value here
and that's really all we need so once
the server starts we can see this
message and i'll go ahead and press alt
z to make it wrap so we can see it i've
got chrome open over here so we can
eventually request a page as well
and we should see this statement in the
console once we start our server i'm
going to go ahead and save the file and
then open a terminal window by pressing
ctrl in the back tick you can also do
that from the terminal menu that's
hidden behind the three dots right now
in my
visual studio code
and now let's go ahead and start the
server by typing npm let me lower case
there
run dev
and this will start nodemon that will
listen for changes
and the server should start up
and we can see that we now have server
running on port 3500 it's not going to
serve anything we're not sending any
type of response back yet we're not
using the response but it will log the
request and the request method so let's
go ahead and type
localhost 3500
and notice in the console we've got the
request here which is just the slash
and then the method is a get request not
a post request or a put request or a
delete request
but a get request
and so we did successfully log those
requests but there's just nothing that
comes back yet i'm going to go ahead and
press ctrl c for now because we don't
need to run the server while we're
making a few changes and now of course
it can't serve a page
but that's no worry we'll be serving a
page soon what we can do is talk about
what else we could put in the server
and so after we're logging the url and
the method and the url was just the
slash as we requested the index page
what we could do
is
build a path and then serve the file so
i'm just going to copy some code in to
discuss it
but this is not what we'll want to do
and i'll discuss why i'm going to go
ahead and expand visual studio code for
now too so we don't have to wrap the
lines
but we could listen for a specific path
so we're defining path here we've got
two semicolons we just need the one
and if it is just the slash or even if
we specified index html either one of
these would work and this is an if
statement
then we could set the response dot
status code to 200 which means
successful
we can set the response header content
type
to text slash html because we would be
serving the html page
and then we could go ahead and use path
to define the path value
and look in our views folder
for the index page that we have right
there
and serve that page and how we would do
that would be to read the file that we
currently have
and i didn't bother with
handling the error in this
version but
what we would do is read the file and
take this data
and then send the data
and we would be sending the contents
essentially of the index.html file this
would work but this is not efficient we
would have a statement
for every address that came in and
actually every file because of course
we'll be serving some files that are not
text html and feel free to try this
version out i'm going to show another
version that we will not use but that
you would also see in a possible simple
server and i'm just showing part of it
here i guess i could go ahead and close
it out
but we could put in a switch statement
and we could look at the request url
value that comes in and here if it's
just a slash we could go ahead and once
again set the status code set the path
read the file and then of course break
out of our switch statement and we would
have to have a case for every value that
came in even a duplicate case
essentially if this were to be
index.html and not the slash and this
would also be very big and you would
have to think of every possible file
that could be requested your default
could of course be the 404 if it doesn't
exist but this also takes up a lot of
space and once again it's not dynamic
and so this is not what we want to do
today either a switch statement is
useful though so let's take a different
approach but we will still use a switch
statement as we construct what our
server does today so we'll start out
once again let's keep the console log
statement with the request url and
method that could be useful as we
monitor things during development and
then let's look at the extension
of the request url that we get and so we
can use the path
dot
name method to get that extension so
let's just pass in the request url now
once again if it's a slash maybe there
will not be an extension name but then
we can handle that as well so now that
we've defined the extension
let's also define
content type
and now we will use a switch statement
to set the content type and i'm going to
paste this in because we'll be looking
at all the different
possibilities for file extensions so
there are several and now we can go over
this but what we see up here above
is we set our extension
and then we define
content type with a let at least we
begin the definition and then we can set
it in the switch statement so here we're
looking at the extension
and now if it's dot css we set the
content type to text css and we break
and we go on and do this for every type
of request that we expect to get or file
content type that we expect to have
requested and you can see there's a
couple of different images here we've
got an image slash jpeg and an image
slash png
text for text slash plain we also have
json which is application json so all
the different types of files we expect
to serve and importantly here the
default
is the text slash html because this
could
not have an extension at all it could
just be the slash
or it could have the extension of html
and so we just handle that as the
default okay the next part if you have
not worked with chain ternary statements
you may not like me too well right now
but i do like chained ternary statements
so i will paste one in and explain it
what i want to do is set the value of
the file path
and now a chain ternary statement like
this one can be confusing especially if
you haven't worked with them much or if
you have worked with them at all but
let's break it down
we're saying that whatever the result is
will be saved here in the variable file
path
so we say if the content type is html
and the request url is just a slash
then this will be the value and we set
the path name
using the views directory as well as the
full path name here we get from the dur
name value
and we set it to index.html
but if that's not the case then we look
at the next condition and that next
condition is here and we're saying okay
if it's an html file type or content
type
then
if the last character in the request url
is a slash
this accounts
for
our subdirectory possibly and not just
the main directory and so this would be
just a little different because we not
only need the views in the file path but
we need the request url that would
specify the subdirectory
and so this is what happens if that is
true
and then if not we go ahead and just
check
now to see if the content type is html
at this point since these other two were
not true
and then we would look at whatever was
requested in the views folder because
that's where the html should be
however
if that's not the case
then we're going to go ahead and just
use the directory name or the file path
and the request url because this could
be
css
or an image or something in one of the
other folders that would be specified in
the request url
so another way to break this down
is everything that starts with content
type is a conditional statement and
everything here that starts with path is
a result if the previous conditional
statement is true and then the final one
the final path because there's two paths
in a row is just the default if none of
the others were true
and so you could break this out into an
if
else if else if else statement if you
want to do that i prefer chain ternaries
myself
and that is what we end up with let's go
ahead and add one more statement here
for the file path and this will be an if
and we'll say if
there is no extension which means it was
probably a slash and didn't have a file
extension
and the request url dot slice
minus one which is the very last
character of the request url
is not
equal to
a slash
so maybe we've just requested a file
like about or new page we have a new
page over here but we didn't
type the dot html afterwards well this
will make that work anyway and sometimes
that's cool to do so this wouldn't
really be required but we're just making
our
addresses work even if we don't add the
dot html at the end so then we'll just
say file path plus equals
and then we'll add the dot html here so
we still serve the correct file although
we might not have typed in the dot html
in the url
bar in the browser so we should probably
add a note to this and i'll just say
whoa i went too far here we go
makes the
dot html
extension
not required in the browser
and that's what that statement is for
okay now we have our file path and
content type so we're ready to actually
check
and see if we want to serve the file and
we can do that with defining file
exists
and setting this equal to fs dot
exists
sync
and let's just pass in the file path
because now
we are checking to see if this file
exists or not so this will be a true or
false value that we get back and we can
check that by saying if
file exists we don't need to say if it's
true we're just saying if the file
exists
then we're going to
serve the file
but if it doesn't exist we also need to
have
an else and there are possibilities if
it doesn't exist this could be a 404
or this could be a 301 which is a
redirect now we still aren't serving
anything yet we have determined our file
path and we have determined our content
type but this is where we would serve
the appropriate files right here for now
let's just go ahead and log to the
console and what i want to log here
inside of the else is the path dot parse
that will tell us the different parts of
the file path
let's go ahead and pass file path in and
take a look at a file path that doesn't
exist so we need to start the server
again i'm going to go ahead and change
this so we can see the browser as well
and then i'll open a terminal window
and now let's go ahead and type npm run
dev
and the server is running we can reload
first and just make sure we get the home
page well i guess we won't get a home
page but we can make sure the request
goes through there we go
and so we'll add the homepage very soon
but let's go ahead and look for
something that doesn't exist at all
because the file does exist so when it
checked it didn't get into that else
area let's just say this is
old right here
okay there was something that didn't
exist i'm going to expand the terminal
window up so we can see this broke out
now and we can see
using the path.parse we get these
different parts
of the file path and what we're
interested in right now is the base
notice it's
old.html even though it didn't exist
that's what we get because it was added
in the file path
so that's what we're going to use
as we come into this area that does not
know if the
file should be a 301 or a 404
and we'll determine that with a switch
statement i'm going to go ahead and
highlight this and just change that
console log now
into a switch
and now instead of just
path.parse.filepath we want to put
dot base at the end of that so that
would be that value
and then we get our curly braces for the
switch statement we don't really need
the semicolon there and we need to put
our cases in here so
our first case would be
old-page
dot html
and this is an instance where we had an
old page and we want to redirect it to
the new page that we see over here in
our file tree so let's handle this
redirect first and this will be response
dot right head
and then a redirect is a status code of
301
and now we need to go ahead and put in
the value here for the header and this
would be location
and then the location is slash
new dash page
dot html
and now that we've handled the redirect
we need to go ahead and end the response
and after we end the response well we
need a little column there
we break out of the switch statement
there and now let's add our next case
our next case is going to be another
redirect
and it's www.page.html
and now i'm just going to copy this much
because it's very similar here for
another redirect
and paste it down we've got a 301 again
but instead of new page we're just going
to redirect to the root which is the
slash
and so that will just redirect back to
the home page once again end and break
and now let's have a default as well for
our switch statement and this could be
larger if you had more redirects to put
in here but the default should be the
404 and so this is where we're going to
serve
a 404
response and let's save this
once again i'm going to expand visual
studio code because that was wrapping a
little weird but here you can see what
we have now we're currently not ready to
serve the new page or the home page yet
we haven't got anything else being
served but we did handle the redirect so
what we need now
is a function instead of repeating the
same function here for a 404
or up here for other files a function
that we can call in both spots okay
let's scroll back up to define our
function and we will put that before the
server and after the port
so let's call this function
serv file because that's what it will do
it'll need to be an async function
and then it's going to need the file
path so we can grab the file that's
needed it's also going to need the
content type so we can set the content
type that is going to be sent and it's
going to need the response object to go
ahead and send that response
so now that we have our parameters
defined and we have the name of the
function
let's go ahead and start by defining the
try block we already know it will be an
async function so there will be a try
and a catch
so we can use await as we get the
information we need let's go ahead and
set the error block first so we'll log
the error to the console
and then we also want to set the
response dot
status code
this would be 500 it would be a server
error here if we could not read the data
from the server that we want
and then we need to end the response
notice i called the parameter the full
word response before we were sending
the res for response right here and
that's what we will pass in as we get
down there further but in the function
i'm using the placeholder here's the
parameter with the full word response
and now in the try block let's get the
data from the file so let's define data
and set this equal to await fs promises
and then dot
read file
now we need the file path to read and
we'll use utf-8
and after we read the file and we are
awaiting that information we can use
response and then right head and inside
right head
we'll go ahead and say
200 status code
and then the content type
there we go content type will be equal
to the content type value that we have
passed in
and then finally we'll have response dot
end and we can just send the data back
now some of you may be thinking ahead
and there are some problems that we will
have with this but this is the very
basic to start out with so i want to do
that first now let's go back down into
the code and call the function where it
needs to be
so after we have the file path and the
file event exists here we say serve the
file so this is where we would call our
serve file
and this should have
the file path value
you have the content type value and it
needs the response object and notice
here it's res because that's what it's
defined as to begin with not in the
function definition of serve file but
what we're using in the server
okay let's just copy this and we'll make
some changes below
as we want to serve a 404 here
so i'm going to paste this in and then
we'll just change a few values
okay here we know exactly what file we
want to serve so we're going to use path
dot join
and pass in the directory name and then
we know it's in the views folder
and then we know
it's the
404.html file and we also know the
content type so we can just set that
to text html right away and then once
again we need to pass in the response
object so let's save that as well now
i'm going to resize visual studio code
again
and we'll go ahead and scroll down and
it looks like of course nodemon's
already working
and yes the requests are coming in and
we served our 404 already because old
doesn't exist so let's go ahead and back
out of here
and see if we can serve the index
and yes the index page serves just fine
too i'm going to go ahead and show the
terminal so we can see all these
requests coming in
and you can see old was a get request
and then it requested the style sheet
requested a fave icon which we don't
have
requested a index page then and once
again the style sheet and once again the
fave icon after that but we still have
some file types that will be issues so
let's take a look at some of those as
well say if we went to our
data folder
data.txt well that seems to work okay
even if we aren't sending the accurate
file type with it but what about data
dot json
that's just text that's not really json
like we would want to send back even
though it looks like it and could
possibly be parsed into it that is not
the format that we really want to send
let's also check our new page
because it should have an image on it
and the image doesn't work either so we
have some file types creating problems
what should be working is the redirect
so let's go ahead and type in
old-page.html
and yes it does redirect immediately to
the new page so that's good and what
about
requesting something in our subdirectory
and yes we got our subdirectory index
page so that also works like we expect
it to let's go back to the data and see
if we can fix that json first so data
slash data.json
i'm going to hide the terminal window
for the rest
of this and scroll back up to
the function that we created serv file
and i'll go ahead
and expand the window once again let's
start by changing data that we've
defined here
to raw data and then let's define data
separately so here we have data and this
is going to be equal to a ternary
statement we'll look at the content type
that we already know
and if the content type is equal to
application slash json
then we'll have a true value that
follows the question mark
and that true value will be
json.parse
and this would be parsing
the raw data that has been read
otherwise the false value would simply
be
the raw data likewise when we send the
response we're going to need an extra
line for this so we'll just do it
on a separate line and keep the
parentheses up here and down here
now what we need is another ternary
statement here and we'll look at the
content type once again
we'll say if it is equal to
application slash json
make sure i spell json correctly then
we're going to have a true and a false
and the true would be
json.stringify
and we can go ahead and pass in
oh not the raw data the data and
otherwise it's just going to be data
that we send oh and i don't need that
semicolon that would cause an error and
we can save our file let's go ahead and
resize visual studio code and reload our
json and now we can see it is served in
a much more expected manner here and
this is truly json that has been
stringified
and of course it was parsed to begin
with now let's go back to new page where
we have a broken image
and we can fix that as well so right now
we definitely get our new page html but
the image doesn't come through now this
is because the image would not use utf-8
encoding i'm going to expand visual
studio code again
and let's go ahead and put these values
on separate lines as well
and now we can close out the parentheses
down here on a separate line too
now we still want the file path here but
instead of just utf-8 we're going to
start out by saying
if the content type
and that is not so we'll say if it does
not include but we're using includes
here
and we're looking for the word image in
the content type because both
the png and the jpg
had the image in the uh content type
that we have down here in our switch
statement and you can see it right here
okay so we're looking for that to see if
the content type includes
image and if it does
or if it does not include image i'm
sorry because we have the exclamation
mark that means if it does not include
it would be utf-8 but if it does
let's just put an empty string because a
string is expected in that spot but if
we don't specify then it will go ahead
and let the image go through so now we
can save this
and go back and check our new image page
let's go ahead and reload it
and we've got our image now that we have
the cute dog image there's one more
change we need to make as i expand this
and if you have thought about this we're
sending a status 200 even when it's a
404 because the 404 processes through
here as well so we need to check we
don't want to send a 200 if it's a 404
so right now in the right head let's go
ahead and break these out as well
into separate lines
just gives us a little more room to work
and see everything that's happening so
now instead of just saying it's a 200
status code we can say if the file path
dot includes once again and if it
includes
404.html
well then we know it should be a 404
and else it should be a 200 so once
again a ternary statement just
determining the status code between a
200
and a 404 here in our function we can
save that and now we are close to being
finished we are serving all the files
and they are having the correct content
type header sent with them but we
haven't logged anything yet so let's go
ahead and
remove the comments here and let's copy
each one of these
and first let's take where we're going
to emit from before we handle the
listener
and as we do that we'll scroll up here
to see the server now right after the
console log where we're looking at the
url and method would be a good place
to emit
and we're going to admit a log but what
we want to say here is a template
literal instead of that generic log
event emitted that we had we'll go ahead
and pass in the request url
and then after the request url
we have a tab delimited log file so
we'll put in a tab
then we'll put in
the request
method
and after the request method we'll go
ahead and put a comma because we're
going to add another parameter to our
log event handler and so now what we
want to say is which file we want to put
this log to and this will be to the
request
log
dot txt so we can save that one
and we'll go ahead and copy the emitter
one more time because we also want to
put the emitter up in the error where we
could catch a server error
and here we'll log something different
so instead of request url this is going
to be
the
error.name
and instead of a tab here
we're going to have a colon and a space
and then we can put in the error
dot message
and we're going to go ahead and log this
to the
error log dot txt
and we can save that too now let's go
back and grab that listener
that we need
and get rid of these extra lines at the
bottom you can see we've used just about
a hundred lines even and let's go ahead
and get rid of one of the lines here
because by the time we finish we will
have 100 even that way
now where we'll put this is right
between the emitter and the port
and we will be listening for a log
but instead of just a message we're also
going to have
a file name passed in
and then here
we'll take the file name in the log
events function as well so let's save
that too
but we need to go back to our log events
function
and here
we can say
it's really the log name so let's say
log name and we need to replace the
event log that we used as a generic
example last time for log name right
here and with that saved we should be
good to go so let's go back to the
server just to have it pulled up and i'm
going to open the terminal window again
and looks like we're already running on
port 3500
so let's switch this over
and now let's reload our new page
and we got several things here including
the log statement because i believe
we're still logging those to the console
so that's kind of cool let's look in our
logs
and i'll make the terminal windows
smaller
and here's a request log so we got a
request we got several requests in
and we logged
exactly what we were getting the request
url and the method and they're all get
methods they're all get requests so what
about an error well we're going to have
to create an error so let's do something
simple like
change something we know is wrong so
where it says read file let's just
remove
the e off of read file
we'll save nodemon will restart the
server
and when we reload we should definitely
get an error
yes and we log the air to the console
because that's part of our function
we didn't get a page served because we
crashed with the error here or at least
it caught it and that's what we should
do
let's look at the error log and this is
what we would want to check out if we
had a problem it tells us of course the
date and time is the stamp and there is
the error name a type error and there is
the error message
fs promises dot read fill is not a
function and that's because we had a
typo with read file so we can go back
and fix that
now being in development mode
nodemon is going to restart due to the
changes and all should be well as soon
as the servers back up and run
so let's go ahead and reload
and we got our new page again of course
we can get our index page as well
and everything seems to be serving as it
should now there are some issues here as
we look at our typical
request log file what really stands out
is these are all get requests we didn't
put anything in our code
to handle any other type of request
really we just assume the request method
is git and of course we would have to
put in
more checks to see if it was a post or a
put or what we might want to do with the
information in interacting with a
database so there could be a lot more to
it but this is a dynamic server that
will serve the typical files you would
have with a website and even some data
files that we have as far as data
json and text file here now
express js makes this so much simpler
but this gives us a lot of context to
think about and what all goes into
creating a web server so i think you
will appreciate express js
that much more once we begin to work
with it now that you have created a web
server with node.js only
here we're at the webpage for express
and you can see the definition of
express it is a fast unopinionated
minimalist web framework for nodejs and
this is found at expressjs.com
now you need to have node installed
already and be able to use npm and then
you can add express to your project and
as it shows here it shows npm install
express and then dash dash save you no
longer need that you can actually just
type npm i
express today we're going to reference
some documentation not from the getting
started link but from the guide and go
to the routing page and this way you'll
know how to find the documentation for
what we're going over today let's go to
visual studio code in the last node.js
tutorial we build a web server using
only node.js i'm going to start with the
completed code repository from that
tutorial and that's what you see right
here in visual studio code but if you
don't have this repository you should be
able to easily follow along without it
or you can download or clone it from the
starter source code link that i'm
providing in the description below okay
i'm looking at the package.json file for
the repository now if you just
downloaded or cloned the repository
you're still going to need to install
the dependencies not that we're going to
use all of these today but if you just
want to get the repository up to where
we are you could go to the terminal from
the terminal menu and choose new
terminal or you can use control and the
backtick that's what i use in windows
mine defaulted to powershell and that's
not what i want let me switch that over
real quick
to get bash that's what i expected it to
be
and once i have a get bash terminal you
would type npm
install
and this will go ahead and update all of
the dependencies or install them if you
don't have them so i'll go ahead and do
that as well and you can see it will
just check my dependencies here it
shouldn't take long and when it's
finished i'll know i've got everything
installed that would be
in this repository so currently i have
nodemon as a dev dependency uuid and
date dash fns which stands for functions
all installed as either a dev dependency
or a regular dependency now what we want
to add is express so we'll type npm i
express we don't need the dash dash save
that's
understood at this point so we want to
install express as a dependency
and it just takes a few seconds and
express should be added and you can see
it here in my dependencies listed
between date dash fns and uuid now just
to update a couple of things for this
tutorial i'll switch the name to
zero six tut instead of zero five and
instead of node.js web server tutorial
i'll just put
express tutorial in the description but
those are minor changes and nothing that
you actually have to do i just wanted to
go ahead and update that let's move over
to the server js file or we'll make the
rest of our changes today now much of
what is here and i can close the
terminal as well much of what is here we
don't need anymore so if you want to
make a copy of this file or if you want
to rename the server.js to old server.js
or something like that to save this code
in your repository go right ahead
because we're going to get rid of most
of it we need to keep the path
requirement the path import at the top
and then i'm going to remove everything
else
down to line 12 where we keep the port
definition
and then i'm really going to remove
everything for now
except
where we listen here at the very bottom
on the specific port now we can go ahead
and make some changes right at the very
top on line one let's go ahead and
import express so we'll define express
and we'll set it equal to require
express after that let's define app now
we could use another name here like
we've used server
but
typically you'll see examples using the
word app so that's what we'll go ahead
and define as well and you set it equal
to express and you call express and so
now we can use the app
where we've been using server so we'll
have app dot listen at the specific port
that we have provided now we'll use path
later it's the only thing we haven't
used up to now and now let's come down a
couple of lines and we can define our
first route so we use app and then we
specify the http method that we want to
route so we'll use the get method here
so any get request that comes in
that looks for
just the root this would be our index
page
then we can specify what to do with that
and this takes an anonymous function
here or a function it doesn't have to be
anonymous but this is what you would
typically see and what i will do
and so now what we do with the route
it happens right here inside this
anonymous function so let's start out
very simply as you might expect and
let's just say response dot send
and then we can say
let me use a single quote just because i
like to better hello world
and that's what would be sent in
response
and so now we can go ahead and start the
server and we will expect to get this at
the index page now to start our
development server if you remember as we
specified in our package json we can use
our dev script that uses nodemon so
nodemon knows to restart anytime we make
changes when we save our file it will
identify those changes and it will
restart the server
js so let's go ahead and open our
terminal again and with the terminal
open at the bottom i'll type npm
run dev and it should take just a second
but the server will start up and we
expect to see that server running on
port message
inside the terminal window let me drag
this up and see what we get
there we go server running on port 3500
so now we'll go to chrome
and open up chrome let's open a new tab
and let's go to localhost and we've got
port 3500
and we get a very small hello world that
is simply delivered in text okay let's
minimize chrome
and let's
drag the terminal down so it doesn't
take up quite as much space but we'll
keep the
dev server running
but let's go ahead and serve our index
file that we have in the views folder
now we do have an index.html here
instead of just sending hello world we
can send a file and you do that with res
which is the response dot
send file now inside of send file
there's a couple of different ways we
can specify
the file to send in express one
is to put in
the path say dot slash views slash
index.html
and then we can provide options
and one option is to specify the root
directory and this is where we can use
that
dame value that is
native to node
and this will tell
node and express exactly where the root
directory name is and then inside the
root directory we would look inside of
views and there we would find the index
file so this should provide the file
let's go ahead and save
and it says it's restarting due to
changes once the server restarts will
bring
chrome back up and now that we've got it
let's reload
and now we've got our index page and i'm
going to resize chrome so we can
just see it and the console when we want
to at least when i pull chrome back up
if i can get it to drop down
not that we really need the whole thing
we don't have much content here
but we've got that so now i'll go ahead
and minimize and pull it back up again
when we need it so that is one way to do
it now let me copy this down
so we can just leave that in
commented
and the other way to serve a file is
what we're already used to doing in node
and that's why i usually do it this way
is we can use
path.join
and then
we specify the directory name
and then we join in the views folder
and then we go ahead and join in the
index.html and this will work in the
same way so let's go ahead and save this
now let's bring chrome back and we can
reload
and we should just get the same result
and we do we still have the index page
so that is more
how i would do it because i'm used to
using path.join but you could do this
either way okay in the file tree we can
see we also have new dash page.html so
let's just highlight all of this and i
press shift alt and the down arrow and i
can just copy all of it down and then
we'll remove the commented outline
and then we'll change our app.git route
here so instead of just looking for the
root we're looking for new dash page
dot html and upon that request of course
we want to switch this to new dash page
dot html as well
and save
now let's go ahead and look at chrome
and see what we get
if we type in our new dash page
dot html so we have slash new dash page
dot html
and we get the new page so that works
just fine so we can tell
that we're not just intercepting the
route right here because this is only
looking for the slash so even though
this has a slash this does make it past
and this is okay we get new dash page
dot html instead of stopping right here
because this does read this down like a
waterfall so it would check this route
first
but now we know it does make it to this
route however if you have the same
question in your mind that i would you'd
be thinking about what if somebody
requests this slash
index.html well this is where express
really helps us out because express also
accepts some regular expressions in the
routing so instead of just saying slash
here we can specify
and we can say this must
begin with the slash and this must
end with the slash
and then we can say
or
and then we can also say slash
index
dot html so if that request comes in it
will route also to the index.html
let's go ahead and save that and once
again look at chrome
and now let's switch back to our
slash without the index
and now if we go ahead and put slash
index.html
we still get the index and if you
remember when we built the node server
we also made it so you could just type
in index without the dot html let's see
what happens if we do that
oh we get cannot get index
so that means it could not find that
page and this is what express returns by
default if it's a 404 and cannot find
that and it's also worth mentioning
where we did a considerable amount of
code to handle things like the status
code and the content type in our nodejs
only web server here express
automatically sets the correct status
code and content type so for that 404 it
already set the 404 likewise for this
html and we're sending an html file it
already sets the correct content type
and of course it sends a 200 if it finds
it as a successful response
now we can add just a little bit more
regex
and that way we can make the dot html
optional
on this request
and if we put the dot html inside of
parentheses and put the question after
it that's exactly what it does so let's
go ahead and do that to our new page as
well
and so we'll put the dot html
and a question mark after it making the
dot html optional in the request
so we'll save both of those
let's come back to chrome
take another look and now index without
the dot html let's go ahead and reload
oh and the server is still restarting as
we see down here restarting due to
changes let's see if it's going to
restart it's taking just a little bit
may need to restart the server
altogether do control c to get out of
that and then i'll do npm run dev again
and we'll get that server running again
okay with the dev server once again
running i'm not sure why it hung up
there let's go ahead and refresh the
page and now we get our index page with
no dot html inside of the url something
else we handled in our node web server
that we created was a redirect so let's
do that with this express server now and
i will copy down the new page request
but now if we're requesting
an old dash page
with or without the dot html at the end
we want to redirect that to the new page
express makes this very simple so let's
go ahead
and remove a lot of what you see right
here
and we'll put in
res.redirect
and now we are sending that to the new
page but we do need to put a slash here
so we have our new page and we'll go
ahead and get rid of that parenthesis we
don't need now there is one thing
missing and what's missing here is the
response code now one will be sent by
express but it's going to send a 302
by default
and a 302 will not necessarily get the
search engine to change
saying it is a permanent redirect what
we really want is a 301 so we can do
that by specifying
the status code at the beginning of the
redirect and then putting a comma
afterwards
and that will go ahead and send the
correct status code that we need because
we would want the search engines to go
ahead and say hey this has been
permanently moved to new page there is
no more old page and we can go ahead and
check that in chrome as well i'll pull
that back up and inside of here we can
just type
old dash page we don't even need the
html
and it redirects to the new page now as
i mentioned express handles these routes
like a waterfall so at the very end of
the route you can essentially put your
default your catch all if you want to
and here once again we'll use just for a
get request
we'll put in the slash
and then an asterisk afterwards which is
the all
and that's kind of like if you're used
to writing sql a select all
so what we have here with regex is a
slash followed by anything will default
to this route let's go ahead and put in
our function as well so we have our
request
and response
and the arrow function and now inside of
here what we can do is essentially send
our file so i'm going to copy this line
and bring it down here and we do have a
custom 404 page and that's what this is
for because express will send a 404
status code and tell you it cannot find
what it's looking for even if you don't
supply anything but we want to supply a
custom 404
and so i'll go ahead and switch new page
to 404 here but what it won't do because
it will find the file to send is it will
not send the 404 status code it would be
a 200
because it could find the file and it
would be serving exactly what we told it
to so it wouldn't know to send a 404 at
this point so what we can do is chain in
the status code here with a status
404
and just another dot so that all changed
together so now we have a response with
a status code of 404 and we're sending
our custom 404 file let's save this and
go back and just request something that
doesn't exist so
up here instead of new page
i'll just put page
and we get our custom 404 what we
haven't talked about yet are route
handlers so i'll just put that here in a
comma
we have route handlers and that's
exactly what these are following our app
get route where we tell it what we are
routing these anonymous function
expressions that we have here following
the route are route handlers and we can
chain those or use more than one of
those so let me give you an example of
that so we'll have another app get and
let's say the request comes in for
hello.html
and let's go ahead and make that
optional as well since we're doing it
with the others so we have the dot html
is optional
and after the quote we put a comma and
now we have
our function expression here the
anonymous function and it's request
response but then we also add in a next
and this is an arrow function
inside this function i'm going to
console.log and just say
attempted
to load hello.html
and then after our console log or our
call to the console log
we call next and what that does is it
moves on to the next handler or the next
expression and you won't see this often
but you can put a comma
and then just put the next
function right afterwards so here we'll
have a request and response we could
have a next if we were going to chain
another one after this
but if this is the last one in the chain
it won't have a next it would just be
the request and response and here we can
response.send and we'll just go back to
our
hello world message that we had at the
very beginning
and if we save this we'll go ahead and
be able to see the console at the same
time let's pull up chrome
and over here we'll go ahead and request
the hello
and now we get hello world in our small
text in the browser
and we also got our console log message
attempted to load hello.html so you can
see
we're calling one function and then when
next is called
it goes on and calls the next function
in the chain now let me show you another
way that you might see functions chained
together and this way is
really the way you would probably see it
more often i'm just pasting in some very
very basic functions here so i've got
function one that has request response
and next and logs of the console 1 and
calls next function 2 same construction
logs of the console 2.
function 3 doesn't have a next it logs
the console 3 it also sends the response
finished
and then it is complete so how would we
use that in a route all three of those
so instead of the commas we saw chaining
the anonymous functions we can say
app.get and let's say this is for the
route let's just call it chain and we'll
go ahead and make the html optional as
well
and after that
you can provide
these handlers in an array so we'll just
have one two
and three
in this array so all the route handlers
are provided
right here in this simple line and we're
calling all of the functions that are
above so let's go ahead and save and go
to the route chain in the browser
i'll switch from hello up here at the
top
type in chain and you can see in the
console we got one two three and in the
browser we got finished in the small
text because it just sent the text
finished and these route handlers work
in a way that is very similar to what we
would call middleware and middleware is
what we'll be covering in the next
tutorial but you'll get used to seeing
this call to next so it can call the
middleware likewise we'll make a change
for our 404 as well because right now
we're just delivering a custom 404
for get requests
and that come in right here with this
path but we can go ahead and improve
that using middleware as well
and we'll also talk about how to serve
those static deliverables like our style
sheet the css any javascript that we
want to go to the browser and anything
else like images that we have currently
broken on the page when we go to new
page or some data so we'll talk about
how to provide all of those as we cover
middleware in the next tutorial
today we'll learn about middleware in
the expressjs framework we're going to
start out with the code repository from
the last tutorial but if you don't have
it you should be able to easily follow
along without it or you can download or
clone the starter source code from the
link i'm providing in the description
below so what is middleware it's really
anything between the request and the
response so the route handlers we
created in the last tutorial are really
middleware too and we can see one of
those right here that i'll highlight on
the page we've got a route handler for
the new page route so you could consider
this middleware and of course we covered
different things you can do with these
route handlers including chaining them
as we see right here i've got a route
handler a comma and then a second
handler or we chain them in an array by
creating three separate handlers here
and then putting them in an array in the
route right here so just a little bit of
a recap but those are overall considered
route handlers even though they are
essentially middleware so what about
creating some specific middle well where
there are three types of middleware
there's built-in middleware there's
custom middleware and there's middleware
from third parties of course the custom
middleware is what we create ourselves
so let's get started with the built-in
middleware i'm going to create some
space right underneath the port
definition and i'm going to paste in
some built-in middleware and the first
thing we should notice here is that is
app.use
and app.use is what we often use
to apply middleware to all routes that
are coming in and of course just like
our http methods get and of course we
could use post delete put and other
methods as well just like those methods
this all
works as a waterfall so if we put
app.use above our routes
then this will apply to all routes that
come in
and you can see in here we're calling
express dot url encoded and then this
receives an options
options object there i can say it
and extended is set to false and that's
usually how you see it you can look in
the documentation if you want to dive
deeper into that but that is typically
what you see so what is this middleware
for
well it's for handling url encoded data
in other words
form data so when that comes in in the
url
then you can pull the data out as a
parameter so we need to add this
middleware that is built into express in
order to get that data when form data is
submitted now i'll add an additional
layer of middleware
and this is for json as you might expect
so not form data but if json data is
submitted we need to be able to get
those parameters or that data out of the
submission and we use this middleware to
do it so once again app.use and then
express.json and now this is applied to
all routes as it comes in and finally
for the last piece of built-in
middleware that we'll apply today we're
going to serve static files and this is
important because if you remember from
the last tutorial we were not yet
applying css we had a broken image in
our new page and files that should be
available to the public were not
available and that's what this
middleware does it serves static files
so once again we start with app use
and then it's express dot static and we
supply the path and here i'm using path
join as we have many times before we
supply the directory name value
and then
the public folder is what we're going to
put all of those in so when we save this
we now need to create a public folder
and inside this public folder we need to
drag our css folder and we need to drag
our image folder
and let's go ahead and create one more
folder in here and let's call this text
and then from our data folder let's pull
down the data dot text and put it inside
the text folder so now we have some
files that we want readily available for
the public and express will
automatically
route those files there because this is
applied before our other routes so it
will search the public directory for the
requests before it moves to these other
routes let's go ahead and open a
terminal window from the terminal menu
you can choose new terminal or like me
control and
the backtick and it opens up the
terminal here i've got a bash window and
that should work just fine i'm going to
type npm run dev to start nodemon for
our development server and this should
launch everything and yes we're already
running on port 3500. let's go to chrome
and i've got a blank window open in
chrome so we can type localhost 3500 was
already there and we get index and it's
already applying the css so now let's go
ahead and comment out
the middleware
so we're not using those static files
and let's reload our index page
and see what we get now there's no css
applied so it is absolutely finding
those public files and applying them
however we didn't really change the path
in those files and we should now i'll
reload this and we do see it's applied
but let's take a look at those files
here's our index and we were going up
out of the views directory
and then into the css directory and
that's not really there so this is being
a little forgiving let's just get rid of
that because we know this path is
available to the public of course after
the directory name there so after the
root of the url it should just be css
style
dot css and we can double check that by
going to chrome
and from there we can just type slash
css
style dot css sure enough we pull up our
css file in the browser so it is
available to the public let's go ahead
and make these same changes
on our new page dot html we can get rid
of the dot dot slash for image for the
image directory and for the css
directory and save that and also in our
custom 404
we can get rid of the dot dot slash and
save now all of those should be working
fine and they really were before but
this cleans it up now let's go back to
the server js and let's consider adding
some custom middleware that we write
ourselves let's create a custom logger
and we want it before anything else
because it we could put it i guess after
the static files are served but then we
would not log the request for the css or
the images or anything like that so if
we put it at the very top
then we'll see the requests for
everything as they come through so we'll
just call this custom
middleware
logger and now we'll start with app.use
and inside of app.use we'll put an
anonymous function that gets a request
response and since we're creating this
it needs to have next so we can move on
notice the built-in middleware did not
need next call it's already built into
that but we need to call next inside of
our custom middleware so let's log to
the console inside this logger
and we'll go ahead and log the request
method where here's the request dot
method
and then give a space and we'll log the
request dot path as well
and now with both of those sent to the
log the only other thing we need to do
is called next so let's save this
and now let's go back to chrome
and we can still see our console here
let's request the page
and you can see we get the get request
here and there's the slash which is the
home page and we also see the request
for the css file while logging to the
console is okay for our middleware
logger what we really would like to do
is create a log file and we've already
created a log events function
back when we were working just with node
so we can go ahead and use this log
events function
in our logger as well let's create a new
folder in the file tree and call this
folder middleware
from here let's pull the log events
dot js file straight into that directory
and now that we have log events there we
really don't need to change much and we
are already exporting log events as well
so that's good what we will need to
change is where it writes the logs
because we don't want it to create a
logs folder in the middleware folder so
if our with our path dot join
what we need to do
is add in one extra spot here i did not
mean to hit return there we go and
just go one directory up with the two
dots
and now that we've got that i'll copy it
and we need to add it to this path
and this path as well so when the logs
directory is created
it is here where we already have our
logs directory from the past rather than
being created in the middleware
directory and now we need to take log
events
and import it into our server so we'll
scroll to the top and let's just put it
underneath path
and we'll say const log events
equals require
dot slash
middleware slash
log events
and that completes that we can save
and now we can use log events
inside of our custom middleware logger
let's put it right above the console log
and we have log events and log events
accepts two parameters the first is the
message and the next is the file that it
should write to or create
so inside for the message we'll create a
template literal and i'm just going to
paste this in so you can see the full
message it is the request method and
then we put a slash t for tab so we have
a tab delimited log file and then it's
the request.headers.org
which should be saying where the request
is coming from what website sent it to
us
and then
we should get what url was requested so
say it's a get method and the request is
coming from google.com and they are
requesting our index page after that we
need to tell it what file to create or
write to
and we'll call this rec for
request.log.txt
and now we should be good to go with our
log events function let's go back to
chrome one more time
and let's go ahead and request our index
page
it was requested we see the requests
here and now we also
see a request log has been created and
there are two requests
it was a get request both times and it
was undefined because we're just running
our node server right here on our local
host
and you can see what was requested as
well the slash for index and the css
style.css
and while this works once again let's
look at the server.js
while this works we're really importing
a function to use inside of our
anonymous function that is our logger
that has the request response and next
as any middleware should so we can clean
this up a little bit let me go ahead and
copy the anonymous function and
everything inside of it
ctrl c to do that and now we'll go back
to the log events.js file and here we
can define
another function just called logger
we'll set that equal to this anonymous
function and now logger can be our
custom middleware that we export so down
here instead of just exporting log
events which we'll use later or we
wouldn't need to right now at all
i'll go ahead and put both inside of
curly braces once again we won't be
using log events anymore inside of the
server js though i'm just exporting this
for use later when the need arises so
back at the server js instead of log
events we'll need curly braces now that
there's two functions being exported
and we'll import logger from the log
events file and now with logger being
imported and all of this logic inside of
logger we can just say app use
logger
and now this is much cleaner and should
still work in the same way so let's pull
up chrome once again
and let's request the index
and we see the console logs still work
let's look at the request log
and yes we have two more requests in the
request log once again notice that it
says undefined here is the origin
because we're just using our local host
server here as we pull this up so let's
make a request from another domain i'll
pull chrome back up
and now
a new tab and we'll go to google
and on google let me go ahead and expand
this and open up the dev tools with
control shift i now that i have dev
tools open
what i want to do
is just call fetch and i'm not really
interested in the data we're just going
to fetch the destination and let's see
what we get
we get a course error it says no access
control allow origin but let's go ahead
and check our log file
and
our log file does show google.com as the
request origin so you may have seen that
error we got before from google that
showed the cores error and that stands
for cross origin resource sharing and
now this will lead us to third party
middleware so we can install a
dependency i pressed control c to stop
our dev server i'm going to type npm i
for install
and then cores cors and this should take
just a second to install and we'll be
able to see the new dependency in our
package json when this is finished
and it says it's finished already let's
check the package json and here we see
cores now added as a dependency so let's
go back to the server.js file
okay so we're in the server.js file here
and we need to go ahead and import
cores so we define
cores and set it equal to
require
cores
once we have that we need to apply the
middleware so we want it to come as soon
as possible but after the logger so
let's go ahead and apply cores here so
we'll say app.use
and then we can just call cores inside
of the app.use i'll go ahead and put a
note here that core stands for
cross
origin
resource
sharing
let's save that and now go back and
start our dev server again with npm
run dev and once the dev server has
started we'll pull up google it says
it's good to go it looks like
and let's go back to google and let's
make the same request
and now there's no cores error we get a
promise pending of course from fetch as
we expect you there but there's no error
and that's the main thing is we can
request data now from the google.com
domain and it doesn't create a course
error let's minimize chrome
and let's look at our request log again
and we once again see google in there so
the domain the request origin did come
from google.com okay now let's go a
little deeper into course i don't want
to make this a full tutorial on cores
because that can get pretty deep however
cores for cross origin resource sharing
is something that does need to be
applied or you will get those errors
however this just leaves it open if you
had an open to the public api
this would be fine you're using cores
and everyone can use it but for many
applications that's not really what you
want so let's create a white list and
we'll define it with const
white list
and inside of that we can have more than
one domain
so let's say https
slash and then www dot your site
whatever your dot com is whatever
application web application domain will
that will access this back end node
server that's where you want to put this
domain here but say you might also have
a variation maybe you've got a variation
without the www dot or
maybe you're working on a local dev
server and you launch from go live down
here click to run live server live
server always runs on http
colon slash
127.0.0.1 which is your computer and
then it gets a port like 5500 i believe
is the default so
maybe as you're building a react app
you're running your react app on this
port or a different port on your local
host or you've used the ip address which
127.0.0.1 is the same as having
localhost there anything like that so
let's add those to the whitelist and of
course we should add
http slash
local host and we're using 3500 here
today now you would take these out after
development but of course you would
leave in whatever domains should be able
to
access your data or your back-end server
and that would be your web application
say your react application is at
whatever domain you would leave that in
here so we've created a list that is
allowed to access the back end that
cores will not prevent so now we need to
create the function that will allow
cores to do this and this is all
contained within the cores options so
that's what we'll call this lowercase
cores and then camelcase here with a
capital o for options and we set this
equal to
an options object and the first property
is origin and origin can accept a
function so we have an anonymous
function here
and it gets the origin which is not the
same as the origin right here
necessarily this is the origin
coming from whoever requested it say
google.com
and then a callback function
and again the two origins a little
confusing but it's right from the
documentation so that's kind of how we
need to do it so we've got the origin
there with the anonymous function and we
can say if
the white list
dot index of
and then we pass in the origin that is
passed in to the function
if that index is not equal to
-1 in other words
if the domain is in the white list
then we're going to go ahead and let it
pass
and then we can do the callback
and the first parameter in the callback
is null which is usually the error so
there's no error and then we set the
second one to true and that just means
that the origin will be sent back saying
yes that's the same origin and it is
allowed and then we can have an else
and in the callback here
we have an error instead of null and we
create a new error and we can just say
not
allowed by
cores
and so that is the message to go along
with the error now that is the first
property here
in the course options we need to add one
more so we put a comma and then we put
lowercase here options
success
status there it is and we send a 200.
and with that we can save
and now that we've created the course
options we need to pass those
in right here and notice we did not
include google in these course options
now google could access without an error
before we put in the options let's go
back and make the same request from
google and see what happens now
we once again have a course error so
let's go back
and let's go ahead and change your site
to
google.com
server is restarting it looks like it's
hanging up nope it's going to go ahead
and do it there we got it
and we go back to google once again and
now it's in the white list
and we fetch and there is no error so we
know our function is working as we
expect it to so when we come back and
look once again you can change this to
your domain whatever your project
wherever it will be hosted say you've
created it in react or view or even
vanilla javascript spelt even whatever
you might create a front end project
with that will access this back end you
want to put that domain in here or maybe
variations of again remember you might
not have the www or it might be optional
anything like that so that allows you to
put in domains that can access the
routes and otherwise cores will prevent
them so very useful third-party
middleware i want to go back to chrome
one more time and create that error one
more time now that we've taken google
out of the white list
at least i thought we took google out of
the white list maybe i didn't save after
we did that that's exactly what happened
i hadn't saved the server js file yet
save here
nodemon is restarting
it's taking it just a little bit
maybe i need to go ahead and restart it
once again every now and then i have to
restart it manually so npm run dev
get the nodemon server up and running
i'll pull up chrome
and let's see if we can get that error
again
so yes we've got the cores error here
now let's look back in
visual studio code as well
and you can see we got the server's
still running but we got this big error
and up here it says our message error
not allowed by cores
and this huge error here now we can
handle errors in express as well but
express is already handling them it has
the built-in error handler by default
and that's what's happening when we
throw this error it is going there
however let's go ahead and add a little
custom error handling
so we scroll down to the very end
remember it's a full chain so we want
this after everything else even after
our 404 here i'm going to paste this in
and you can see i'm using app use once
again and then this is an anonymous
function
and it doesn't just get the request
response and next it also receives an
error parameter
and then we can log to the console the
error stack if we want to or just the
message or whatever we can go ahead and
send a status 500 server error and we
can send the message to be displayed in
the browser so i'm going to save this
and we'll go back to chrome but we won't
use google anymore let's go ahead and
just use the page that we have here and
i'll see if i can resize there we go i'm
going to reload
and look we got a not allowed by cores
even though we requested our index page
but our custom error did work and it
sent this to the browser now why did we
get that well let's go ahead and look at
the log here's our request log
you can see it's once again undefined
so undefined is a problem because that's
not in the white list so during
development you want to go ahead and
make one modification to this function
right here after the minus one go ahead
and put in the or operator and then
say no origin essentially the
exclamation mark
origin which would be the equivalent of
undefined or false
and we save that and now it should work
now after development we probably want
to remove that as well just like we want
to remove
some of the
development urls that we have up here in
the white list so now let's go back to
chrome
and see if we can reload our index page
yes and now everything is working as we
expect it to and we can also tidy up
that custom error handler so let's
scroll back down to it
and we see the function that we have
here inside of app use i'm just going to
copy that complete function
and now inside the middleware folder
let's create a new file and let's call
this error handler with a capital h
there so camelcase
errorhandlerjs and now let's define our
error handler
and set it equal to
and we'll remove
the function keyword and we'll go ahead
and put an arrow after this
and we've defined our error handler but
what we want to do that we're not doing
yet is also create an error log and this
is where exporting the log events
function that we left here
comes in very handy so now we need to
import that here not the logger but the
log events so let's define
log events inside of the curly braces
set that equal to require
dot slash
log events
and now that we're importing log events
we can go ahead and use that in the
function as well so we'll call log
events right here before we log to the
console and once again this function
gets a message first and then the name
of the file that it needs to write to or
create so this will be a template
literal and i'm just going to paste in
the values once again here instead of
anything from the request such as the
method like we were logging before we're
going to log the error name
and the error message and then we'll put
a comma and go ahead and specify error
log dot txt as to where we want to write
the error to and we can save this but
we're not quite finished yet because we
need to export this function so we do
module dot exports
set this equal to
error handler
and now save and now we need to import
error handler into our server so let's
scroll to the top of our server js and
right underneath logger i'm just going
to go ahead and copy this down
and let's change this
to
error
handler
and instead of log events we're
importing from middleware
error handler
and save
now we need to use the error handler
where we previously had the larger
function in this file so it will be much
cleaner so let's go ahead and select
this full function
delete that and paste in
error handler
and we can save
and we got an error the app crashed
let's see what we've got so if i scroll
up
it says app.use requires a middleware
function so something about the function
probably didn't get imported correctly
let's go ahead and scroll up to the top
and look at our import statement and oh
yeah we were using curly braces around
logger because it was one of two
functions from the file but from the
error handler it's the only one so let's
go ahead and remove those curly braces
around error handler and see how
everything goes it's restarting the
server right now and server running on
port 3500 that part looks good so with
the server running the best way i know
to create an error would be to go ahead
and remove this or
no origin for now
and we'll go ahead and save and then
we'll try to reload
the index page once again in chrome so
it's starting the node.js server
and now that it's running let's reload
and we've got a not allowed by cores as
we expected an error in the console
and now we have an error log file over
here in the file tree and we can see we
got our error not allowed by cores
at
1827
so let's go ahead and look at the
request log and we should be able to
line that up with what request happened
here as well and we can't so everything
there is working as expected so now that
the error is logging let's go ahead and
put that or
no origin statement back in for now so
we do not keep getting rejected by cores
we can go back
and we can once again request the index
page
and everything should work well when the
server restarts there we go we've got
the index page now okay there's one more
thing to do before we're finished today
and that is to compare app use
to app dot all and that's what we're
going to do right here as we change up
the 404 just a little bit so with app
use
we could do something like this
app use and specify
here at the end of the chain everything
basically that came in
from the slash which would be the root
but app used does not accept regex and
also app use overall
is more likely to be used for middleware
but app.all
is used for routing and this means it
will apply to all http methods so it
also does accept regex so what we can do
inside of this is just put an asterisk
it's at the very end of the page
so we can just say pretty much anything
that made it here
should
get the 404 at this point let's go ahead
and save the file and test it out i'll
pull up chrome
and here
we can put in the
slash dave
and yes there's no page that's dave html
so that is a 404 although the request
did log just fine we can customize our
404 response just a little more so let's
start that out by just setting the
response status because we know it's
going to be a 404 no matter what
after that let's check the type so we
can say if
the request
accepts
and now we'll look at the type the
content type and we can just say html
here and express will translate that for
us and after that
then we'll put what happens because if
this will resolve till true or false
here so
then
we can go ahead and look at sending a
file or something else inside of here so
what we'll do is say
response
dot send file and go ahead and send that
404 now this is very much like what we
were already doing but now we can check
for other types as well that might be
accepted so let's go ahead
i'll just copy all of this down and
we'll change some of it so shift alt and
the down arrow copies everything
and now we'll say if the request accepts
json well then we don't want to send a
file at all so this will be entirely
different
let's get rid of all of this
and say response.json
and we can put some json here inside of
our response and let's just say error
and over here
we can say 404
not page let's just say
not found which is essentially what a
404 is and really what we're doing is
creating a small waterfall effect here
with the if so we could make it another
else
if here and then finally
an else for our last
possible outcome
once i can spell else and here we'll
just respond and we can set the type
to text
just txt for the type actually and then
we can
send and inside the send we'll just put
the same text that we have up here
404
not found so i'll paste that in and that
should work as well so now i'll go ahead
and remove these blank lines and we will
test out our 404 once again
i'll go to chrome
and i've already got
slash dave in the url so i'll just see
if i can reload this 404 once the server
restarts
and now it's restarted i'll hit reload
and we got the 404 and it is html
now i would have to use postman or
something else to ex try out the json
file or possibly text as well we would
change the headers for that and i'm just
going to let you trust me that that will
work for now instead of breaking out
postman or anything to just try out
those two other routes but overall that
is what you can do to customize a 404
with app all and i hope you understand
at least a little bit of the difference
between app.use which is for
middleware and does not accept regex and
app.all which is more for routing and it
will apply to all http methods at once
today we're going to talk about creating
routers in the express js framework and
we're going to start out with the code
repository from the last tutorial but if
you don't have it you should be able to
easily follow along or you can download
or clone the starter source code from
the link i'm providing in the
description below alright in visual
studio code we've got our server.js file
and from the previous tutorial we've got
a lot of code in here and really what we
can do is break these routes out into
individual routers that are essentially
mini servers for each specific route or
many apps as we create our app with
express here these will be mini apps and
we'll for refer to them as routers so
over in the file tree let's create a new
directory and let's call this directory
routes
and now inside the routes we can create
routers for each route that we are
handling so let's create a new file here
and let's call this
subdur.js because
we have a subdir directory in our views
folder that we haven't handled at all
yet
now in the subdir dot js we need to
start out with a few requirements once
again and the first would be express
just like we did in the regular app or
server.js if you will so we have require
express
and then after that just like we have
before
we have applied express however now
we'll define a router instead of an app
and we'll set this equal to express dot
router with a capital r
and then we're going to go ahead and
need the path module as well from
common core module in node.js
so we'll require path
after that
let's just go ahead and put our module
dot
exports and set that equal to router
because that's what we'll always do when
we create a router and then we can
import that back in the server.js when
we need it now we're ready to just pull
our routes in so let's look at the
server.js
and the routes that we had or did not
have at this point for the subdirectory
and i don't believe we had any yet we
were just routing to our specific views
for index and the new page and of course
we were redirecting the old page so we
hadn't handled anything for the
subdirectory yet it has its own index
and it has a file called test html so
i'm going to copy how we handled the
index for the root
and then we can just modify that in the
subdirectory
and instead of app.get we now have a
router so let's replace app
with router and now it's router.git and
then we can handle this slash or index
for the subdirectory the same way we did
for our root and we have a request and
response and we can get remove this
commented line that we had in there
before
and then we are sending a file and we're
sending the root directory we get the
directory name here
and then we want to have views but now
we have to think about this because
we are not
inside the root so we can't just look
directly into the views directory so we
need to come up out of the routes
directory we created and then into the
views directory and then
we're not only just looking for the
index because that would be the root
index now we need to specify
the subdirectory that we have
and then the index.html that's within
that subdirectory file
after that let's copy this down and i
can press shift alt and the down arrow
i'm using windows it may be different if
you're on mac or linux
and now we have another git route but
this will be for
the test dot html and so we can replace
index with test and remove the initial
slash we'll leave the html optional this
is regex that we can use within the
route
and now we're sending a file once again
but instead of sending index we're
sending
test not text dot html and we can save
that now that we've created the router
file for the subdirectory we need to go
back to server.js
and
underneath where we serve the static
files and we set that
we need to go ahead and provide a route
and we do that with app.use
in the server file and now let's provide
the route
that we are providing the router for
and then we can go ahead and require the
router that we have created and now this
will be in dot slash
routes slash
and there's the sub directory and after
we provide that we should be finished
and now this will route
any request coming for the subdirectory
to the router instead of the routes that
we are providing below through app.git
such as index and new page so this will
match in front of these and of course we
can put all of these in a router and we
will next but let's go ahead and start
the
dev server i'm going to press control on
the backtick but you can go to the
terminal menu and open a new terminal
window
and if you remember we type npm
run
dev and this will start our local dev
server with nodemon which will listen
for any changes and it's on port 3500 so
let's go to chrome
and i'll go to my blank page i'll type
local host
port 3500
and there we've got our index file and
that's fine but we want to go to the sub
directory and see if this is working so
we've got sub
dur
and yes we've got our subdirectory index
page and let's go ahead and see if the
test index page in the subdirectory is
also working i said index test.html
actually and there's our test page in
the subdirectory as well okay now let's
ask for a page that does not exist
in this sub directory and let's see what
we get
well we've got our 404 page but there's
something important to notice here in
the past when we've got our 404 page it
had css
supplied for it now we didn't put css
on the previous subdirectory pages that
was not included anyway but if we
requested a 404
on any other page that was not in the
subdirectory
it does have css applied so why is the
css
not working for our subdirectory well
it's because we haven't told express to
use
the public folder or public directory if
you will for the subdirectory so let's
copy this line down with shift alt and
the down arrow once again and now we can
specify the subdirectory here
and tell express to use that public
directory before we didn't supply a
directory but the default is the slash
anyway and so that's essentially what we
were doing but we can save these
and now we should be able to go back and
request our 404 basically any page that
doesn't exist in the subdirectory
and probably get the formatted 404 yes
now even though we're in the
subdirectory and i requested a page
named dave that doesn't exist
we got a 404 that actually has the css
supplied you can even see the request
coming through down here for the css in
the sub directory and it now works now
i'd like to make one quick correction
from the last tutorial that i did
provide information that was correct in
older versions however in newer versions
of express express does support regex
inside of app.use and i said it didn't
when i compared app.use to app.all but
now you can have regex there and so if
we are to require a router
and we put a slash with an asterisk here
this would supersede the subdirectory
router in other words all
routes would go to this router if we
supplied that of course we don't want to
do that so we want to specifically state
just the slash or we could even get more
specific and we could say hey it must
start with the slash
and it must
end with the slash
and then of course we would only be
providing that for the slash right here
but
that is because it supports regex and in
previous older versions express did not
support regex inside of app.use but in
the route it now does let's go ahead and
create a router for this
root directory here so let's say dot
slash
routes slash
root
and now add the
semicolon one more over
and save and now we need to go ahead and
create this route because right now it
doesn't exist
so let's go over here to the file tree
i'll collapse the views folder we don't
need to see what's open there but inside
of routes let's go ahead and create this
root
dot js
and now inside of the js file
let's go back to server and grab the
routes that we were using
for the root and we have our index page
our new page
and a redirect for an old page and
control x will cut those out and we can
paste those
inside of the root but we also need
those imports and basically
everything you would normally start out
with
for a router these three imports at the
top
express router and path
so we'll put those at the top of root as
well
and then at the bottom
once again you want to have module dot
exports and set that equal to
the router now what haven't we changed
yet is the keyword app here that we were
using in the server.js we need to switch
to router as well
we can once again get rid of this
commented file
and we have to once again consider the
route that we're providing because now
we're in the routes folder so yes we
have the directory name value but then
we need to come up out of
the routes folder
and then look in the views folder now
this will be correct now we're not
looking in a subdirectory so we no
longer have to provide it there let's go
ahead and copy this
and paste it into the route for the new
page as well however the route for the
old page is a redirect and this will be
okay to leave it as it is so let's save
the root
and let's go back to the server we can
also get rid of some of these examples
of route handlers that we had such as
the hello right here and the chaining
the route handlers that i had previously
provided for the chain route let's just
delete all of that to clean it up a
little bit
and save our server.js
it's now restarted so let's see if these
routes are now working with the new
router
and we'll come back and just go to the
root
and yes we got the root page
let's go to slash new dash page
html should be optional and there's our
new page and our image is working which
means we're getting our static file
routed correctly to the public directory
where the image is stored and now let's
go to old page and see if the redirect
still works
and yes we redirected back to the new
page let's go ahead and type in index
instead of just the slash and yes we
still get the index page everything in
the new router for the root is working
correctly with our routes working
correctly
let's talk about how we would set up an
api or a rest api if you will
and that's what is more important when
we're working with say the mern stack as
say
express node and of course react
on the front end or you could substitute
any database technology for but
we've got node and express here
and we want to organize
a api which is what we would more than
likely create with node and express say
compared to
a static server for web pages although
you can do that
it's much more likely that you'll create
an api and that's what we'll focus on
here so
underneath these routes we're going to
create one more route
and this will be
to
employees
and then inside of routes it's going to
be in a subfolder named api
and then the employees and notice this
will just be routed by the user going to
employees they will not have to type api
so let's go ahead and save this and then
let's start creating that route inside
of routes we want to create another
directory
and that's api
and then inside of the api
we want to create our employees router
and that's
employees.js now up here inside the data
folder we've already got a file named
data.json let's go ahead and rename this
file
and let's call it
employees.json
and let's give each employee inside of
this file an id
and we'll just make these chronological
so we've got one
and an id of
two
and now that we got that we can just go
ahead and save our small employees data
file and remember that's employees.json
inside the data folder
and we've created the route here inside
of server using app use now it does not
need to have any static files as we'll
just be delivering json data from our
api so we wouldn't typically have any
static files like we would with a web
page so now let's go back to our
employees.js
route let's start out our route just
like we did the others so we want to
define express we'll set this equal to
requiring express
and then we want to go ahead and define
our router once again and we'll set this
equal to express dot router with a
capital r and call router into action
and we'll once again need the path
common core module from node.js so we
can require
path
and after path let's go ahead and define
an empty object named data
and from there let's set data dot
employees
equal to
require and now let's get that json file
so we need to go up out of the api
folder and then we need to go up out of
the routes folder
then into the data folder and then to
get employees.json
and so this is kind of like connecting
to a database this is just what we're
doing for this tutorial and and dev of
course in the future
we'll work on connecting to
and of course you could replace that
with your database technology of choice
but once again just for this tutorial
just for dev we're just going to import
some json to simply work with here so
now let's go ahead and create our
module.exports
and set that equal to router again as we
always do with a router
and now we can set up our routes now
instead of just saying
router.git and creating
anotherrouter.post
and another router dot put
and of course putting those paths in
there like we could
one thing we can do is say router dot
route
and then provide the route and then we
can chain the different http
methods that we want to provide for this
same route as a get request and a post
request can both go to the same route
but there can be a different result for
each so let's handle the get request
first and here's a request and a
response
and now inside the handler of the get
request
let's just return json and let's return
all the employees so that will be
data.employees
now for the post request
we can just come down here and say
dot post and once again
we can have a request
and a response
we can handle this completely
differently for the http method post
than we do for git
and now with the post we'll get
parameters coming in
to the request and we can refer to those
parameters with request dot body and
then dot whatever the name of the
parameter is
and we had a first name and a last name
and a post request would be posting a
new employee
so what we want to do i'm not going to
put in the full code that we would have
in an api at this moment because it's
not really about coding an api right now
we're handling routes so we just want to
see how each one of these would work
so let's say the first name that we
return in the json will return what is
sent and that's request dot body dot
first name
and then let's go ahead
and return the last name as well
that's request dot body dot last name
once again not writing out all the code
for an api at this point just showing
how we can get
the parameters from a post request and
then we're just sending those parameters
back but this is how we could handle
each route in an api
now let's go ahead and do the same thing
with a put request
i have a request and a response and this
would be
the http method that you would use when
you're updating an employee for example
but we could do the same thing we could
get the
res.json here and as a matter of fact i
can just copy this down
and of course the code would be
different if you were writing an actual
api because you would be updating at
that point and we'll do that in the near
future just not going to do it yet and
then let's get a delete now a delete
would be different we wouldn't be
getting the first name and the last name
we would be getting the id
so we'll just send the id back in this
case
and that would be response.json once
again
let's go ahead and send the id back here
and this would be
request.body
dot id
now you can see we've chained each http
method together and we've got git post
put
and delete all coming into the same
route on our router they're just
different request methods and of course
this being the last one we'll go ahead
and stop
right there and i'm going to save this
but we're going to add one more route we
have the route coming in with just the
slash just the root but then also
we could have
router
dot route
and then a slash but we could have a
parameter directly in the url and that's
what we're showing here is a parameter
and this would be
a get request that has a parameter
inside of the url so now down here and
of course you could do that with other
requests too we're going to use a get
request so let's go ahead and put
the handler for our get request here we
have our request and response
and now inside the handler will once
again respond with json
and we'll just say
here's the id
requested and now this is different
instead of request.body it's
request.params.id
and that's because we're pulling the
parameter
it's a named parameter actually and
we're pulling it directly out of the url
so here we're responding once again with
just what we received just as an example
now i'll put in p future code will go
ahead and actually code out an api right
now handling the routes and so all of
this is inside the router and i guess we
didn't need path right here at this
moment so we can get rid of that but all
of this is inside the router for
employees let's go ahead and save this
and we can test it out and our server is
still running below so we can now check
these routes with thunderclient
thunderclient is an extension
in the past i've used postman which is a
separate program to test apis let's go
ahead and try this extension out you can
find it under extension search for
thunder client
and it allows you to save different
routes you want to test
so right here i'm going to go ahead and
choose the
get route that i created first there it
is for all employees which of course
there's only two and you can see
here is our http method and here is the
route and now i'll click send
and here's the response i can just make
the response full screen but we've got
both of our employees returned
and that's exactly what we expected so
now after testing the get route let's go
ahead and test the post route so i'll
choose that from our list well let me go
to collections i believe i saved
everything there there we go
let's choose post
and now we're once again going to the
employees route but it's got post
let's look at the parameters and see if
those were saved
and
it should be under body actually there
we go and i'm posting a new employee
named john doe
so let's see if we get that in return
we'll send that and let's look at what
was returned yes we got john doe
sent back to us and that's all we're
going for today once again as we're not
writing out the actual code to add the
employee or update the employee and
things like that in the near future yes
i will okay
put should be the next one to test so
now it's a put method at the same url
and once again we are sending some john
doe oh actually sending just the id
and the first name of what we wanted to
update and of course i think i had the
put request just sending back the first
name and last name so maybe we'll just
get the first name back let's see what
we get when we test
put
and
yes we just got the first name back and
that's what i expected so that works
also
now let's test the delete route of
course it won't actually
delete the employee this time around
and looks like we're sending just the id
and we should get that id back
expand and yes in return we've got the
id back so that route is also working
now there's one more git route where we
could get a specific employee
and let's see if that one's here or here
here we go so
this is our route with employees but
then it has slash and the number one
where we're requesting
just one employee not both so let's go
ahead and send that and see what we get
in return and we returned the id of one
so that route is also working and again
this extension is called thunder client
if you want to check it out for testing
the routes in your api now as a
challenge before my next tutorial you
could go ahead and write out the code to
update the
json that we were receiving and of
course just hold it in memory don't
really update the file but you could
test out what you want to do here for
each of these routes and you could
handle each one of those and i'll come
back with some code for that in the next
tutorial and what i really also want to
talk about in the next tutorial is
organizing the rest of this api
including the server file
and setting it up because after that
we'll pretty much be ready to connect
everything to whichever database
technology we would want to so we'll
organize this with an mvc pattern that
is model view controller and of course i
can come back and add some extra code
here to handle the api data just for the
json as an extra test it's not really
anything to do with specifically express
that's just javascript in handling how
you would
basically do crud operations create read
update and delete
today we will organize our node.js and
express api server with the model view
controller design pattern you may have
also heard this design pattern referred
to as mvc that said remember express is
by definition an unopinionated framework
and you can organize your project
however you would like i'm just
demonstrating mbc because it's a popular
pattern we're going to start with the
code repository from the last tutorial
but if you don't have it you should be
able to easily follow along without it
or you can download or clone the starter
source code from the link i'm providing
in the description below okay we're
right here in visual studio code and
we've got our server.js file and we want
to use the correct terminology so we
were keeping our data inside of a data
folder and you can see i've renamed it
to model it still has the
employees.json file in there and we're
just emulating a database here with a
employees.json file instead of
connecting to one at this point because
you might have my sequel or in
mind or whatever later on in the series
i plan to show however right now
we're just going to simulate that
connection to a database
and now besides that we have a views
folder and really with a restful api we
won't have many views if hardly one
maybe we would have a welcome page that
gives some directions about the api but
that's about it we're not really serving
a bunch of static pages or resources
what we will have though is a controller
folder so let's go ahead and create that
when i said controller and really i
should make this plural because it could
host more than one so let's go ahead and
call this controllers and it will hold
controller files so inside of here let's
go ahead and create an employees
controller
dot js file now currently we have all of
our logic really inside of our routes so
if we open up routes and the api
directory and then look at employees js
our logic is essentially the route
handlers so
for router dot route and then we come to
the get http method we've got this
handler that returns all of the
employees so what we would want to do
would be remove that logic from here and
take it to the controller so we could
name this something like
get all employees
and then we can just assign
that route handler as a function so now
we have a get all employees function
that is part of the controller so let's
save that but what we also need here is
the data so the data that we were
pulling in to the employees js route
inside the api
is actually will need to be inside of
the controller at this point instead of
the route we're breaking that out
and so we'll put that at the top of this
file as well so these functions can work
with that data so i'll save this again
and now back inside of employees let's
pull out the logic from the post
and here we go with that logic and now
we'll call this
function a different name we've got
create new
employee
and we'll set it equal to that action
and then the next function will go ahead
and name as well and that will be update
employee
we'll set it equal to what we find here
in the put
portion of this route
and now we'll come back and assign that
to update employee
and finally as you might guess we'll
have
delete employee
and we'll set that equal to the handler
for delete
not copy but cut there we go
and we can paste that in here as well
now i'll go ahead and save that now
we're not finished with the controller
by any means but we have the
first actions in here for these routes
and we haven't put anything back in here
yet but we will but there's one more
function to create here and this would
be called just git employee instead of
get all employees so let's go ahead and
get this
action as well
or route handlers what we could refer to
those as still
and let's say git
employee
and now we have all of these different
functions defined inside of our
employees controller
and of course we're pulling in the data
here at the top but as you might expect
we need to export these as well so we'll
have module dot exports
let's set this equal to an object now
and so we had get
all employees
and then we had
create new employee
and we had update employee
and
delete employee and then we had get
employee and so we're exporting all of
these functions now let's go back to the
employees js
routes and let's import our controller
so let's call this
employees
controller and we'll set it equal to
require and then we have to consider
where we are so we go up a folder
and then up another folder and once
we're there now we can select the
controllers folder and inside there we
find our employees controller now we
could deconstruct those functions here
as we import those in but i think i'll
leave the name employees controller
because we're emphasizing creating a
controller here and then we'll just put
in the functions and in visual studio
code i believe if i start typing the
functions
it's going to import the part that says
employees controller for me sure enough
it does so now we can have create new
employee and select that
and here we're going to have
update employee
and finally we should have
delete employee not this delete we
didn't quite get that right
delete
employee there it is and so all of these
actions are coming from the controller
or route handlers we could say and this
still doesn't look right we didn't get
we've got a d in front of there
employees controller dot
delete employee that's what we should
have and now here we should have git
employee
with the employees controller so this
definitely cleaned up our route file
here quite a bit and we've separated the
logic into our controller and that's
really what the model view controller
pattern is all about and so we could do
that with other routes if we had other
routes as well but now we've really
cleaned up our whole project let's go
back and do just a little more cleanup
in the server.js and we can get rid of
some of these views that we're no longer
going to use one other thing i'd like to
do is you see we're pulling some things
into the server file right at the
beginning and we're starting to use the
logger right away but we're taking up a
lot of space here with cores and we
don't have any kind of configuration
folder over here in the file tree yet so
let's go ahead and create a
config folder not js just config folder
and now we can go ahead and create a
course options
dot js file right there
and back in the server let's go ahead
and take our white list and cores
options
and cut those out of here
and paste them into our cores options js
here and i'm going to put each one of
the urls on its own line
just so it's easier to read
and after we define the white list at
the top we have our cores options
and as you might guess we need to do
module dot
exports
set this equal to cores
options
and now we'll need to import this back
inside of the server
so at the top of the server here we
could just put it right under cores
and we'll say
const cores
options and set this equal to
require dot slash
dot slash there we go config
and now inside of the config folder we
have our course options
we can save that let's see if there's
anything else we can clean up i've got
some extra comments in here of course
this being a tutorial that kind of makes
sense
let's make them a little bit smaller
let's put form data besides that
and the routes look good and we have our
catch all 404 here
and we have our error handler and we
have made our server.js much smaller
we're down to 44 lines of code including
some spaces here and we have pulled that
logic
out into a controller so that has really
helped clean up but we're really not
going to need these example of a
subdirectory anymore so let's go ahead
and get rid of that
and that means we can get rid of the app
used for the static files in the
subdirectory as well
and save that once again and that means
we can
delete
the subdirectory route from the tree
and we can also go ahead and delete the
subdirectory over here in the views
and let's go ahead and delete the new
page
and that means we can go into our root
routes
and we will not be routing to a new page
or an old page any longer either and get
rid of both of those
and now that's quite a bit of cleanup
but at least we still have our splash
page for the index if we wanted to put
some directions for our api there
and so we're handling that inside of the
root route
and that's in our routes folder and then
we have our api directory and that
handles the employees routes here
okay with everything cleaned up in the
last tutorial i promised to go ahead and
provide the javascript code this is not
so much just dealing with express or
node but the code if you were to have an
api that was going to update
not the json file but just in memory and
kind of emulate an api before we've
connected it to a database so at this
point i can go ahead and provide that
code and if you had went ahead and
worked on the challenge that i said at
the end of the last tutorial you can
compare your code to what i did i'm just
noticing i didn't change the path to the
data folder here either for the
employees because really it didn't need
to come up to anymore so you would just
come up out of the controller folder and
then you're not even going into a data
folder at this point you're going into a
model folder
but i'm going to change this all of this
now but i wanted to say that correction
about the path
and let's have the data
object set like this and if you work
with react i'm thinking in more of a
reactive way when i do this but i've got
the employees data set still pulling it
in and just setting it directly to the
employees property of the data object
but then i've got a set employees
function here as well that i'll use
inside of these other functions now as
you might expect get all employees
doesn't really need any extra work we're
just returning all of the employees by
referencing data dot employees and
that's fine now to create a new employee
i'll paste in my code and we can review
it
there's a little bit more work there so
when we get a new employee we want to
create the new id and instead of
importing a package like uuid or
something like that i'm just grabbing
the last employee
in the json array
and i'm adding one to the id that exists
there whatever the last one is so of
course i need to make sure they're in
chronological order which will come into
play later but that's what i'm doing
here other than that
first name and last name assigned to the
parameters we're getting from
request.body
and then of course it's first name and
last name
after that we're making sure
that a first name and last name are sent
and if not we're sending a response
400 saying hey
we didn't get the required data after
that i'm using the set employees
function to go ahead and set the
employees to the new information and of
course the new employee as it at the end
of the array and i'm just realizing here
i didn't send a different status when we
send the employees back and i'm sending
all of the employees instead of just the
new ones so we could see the update but
here we could also send a status of 201
which means it created the new record
and we'll go ahead and save that okay
update employee gets a little more
complicated but it's not too bad so
let's go ahead and paste this code in
and review it
so here we grab the employee if we
received an id
as a parameter and we should because
we're updating an existing employee and
if the employee does not exist we're
returning a response status 400 meaning
we did not get good data to update we
have the message here as well employee
id is not found and that would be the
end of it if we didn't get an id but
then if we did then we're checking to
see if we got a first name go ahead and
set the employee that we found to the
new parameter value and the same for
last name and of course to that
parameter value as well
and then we filter the array and we
remove
the existing employee record from it so
then we have an array with the existing
or i mean without
the existing employee id and then we
have the new
employee
data that we've updated here so we go
ahead and create an unsorted array and
we need to remember it's unsorted
because we do need this array in
chronological order by id
and so when i call the set employees
function from our data object i go ahead
and sort the array by that id column and
this is a chain ternary statement this
is where it could look a little
complicated if you have not worked with
sorting arrays before so
we start out with the ternary statement
here same pretty much if
the one element of the array dot id is
greater than the other than return one
and normally if we were just doing that
then if not it would be returning a
negative one but we need to have a zero
if they're even as well so then we chain
this into another ternary
and there
it returns a minus one
or the zero at the end of that
and then of course we send the employees
after they've been updated by set
employees and that puts the array the
json array back in the order we need it
right there okay scrolling up to get a
little room to put in the code for the
delete employee
we paste this in it looks very similar
we find that employee but if we didn't
get the id we send the 400 response just
like above we also create a filtered ray
a filtered array just like above we did
in the update but then at this point
we've deleted the employee so all we
need to do is set the employees to the
filtered array and then we once again
return the employees and finally the get
employee and of course
this is
handling a request for the data of just
one employee so we find out who that
employee is and once again if the
employee doesn't exist we send that same
response that we did not get good data
but if the employee exists all we need
to do is return that specific employee
and then we're down here to the exports
so i will include all of this code
inside of
the git hub repository for the end this
will not be the starter code but the
beginning and then you can see this and
compare to years if any of my
explanation did not make sense and it
looks like i've got an error here in the
code i think i missed the ending curly
brace there it is they should be yellow
at least the way i have my visual studio
code set up
tab that over and save and all looks
good now i think i still have an issue
here the delete employee did not close
out
if i close that out
then i can remove that there we go
almost had a bigger mistake that would
have taken me longer to figure out so
save that and now it looks like get
employee delete employee update employee
create new employee
get all employees that all looks like
they're defined correctly and there's no
more error here in the file getting rid
of the extra space and let's go to
thunder client but we also need to go
ahead
and start the server up because i don't
think we have done that yet
and with the dev server open
type in i mean the terminal open we'll
type npm
run dev
there we go
and start the dev server and then we'll
be able to test out our api running on
port 3500 that's what we needed we'll
open that employees api
collection that i'd created in thunder
client and now this you'll have to
create yourself of course if you haven't
but let's test get employees and see
what we start with there so we've got a
get request and we're going to the
employees route
see the results and we've got the two
starter employees dave gray and john
smith
okay now let's go to post and let's post
a new employee and if we go to body you
can see i'm sending some raw json here
we're posting employee john doe
we'll send that post request there's a
201 created response like we wanted to
send and now we have john doe here and
he has an id of three as we expected him
too
so now let's go to the
put employee
and
same route with a put request we'll go
to the body and see what i'm sending in
the json
and let's change this let's update
the second employee so it's right in the
middle
and change his name to david
now let's go ahead and send and now
let's look at our employees that were
sent back and yes second employee is now
david smith and they're still in the
correct order
he did not get inserted at the end it's
not out of order and so
that all worked as we wanted it to
now let's try the delete employee
and let's look at the body once again
what's being sent in the request and
we're going to delete employee number
three so let's go ahead and do that
and now if we look all the employees
were sent back now we only have two
employees once again and now let's
finally request an employee this is get
an employee we're going to request
employee number one
send that request and it looks like we
received the information back for
employee number one so all of our routes
are working as expected once again i'll
include everything that you saw today in
the employee controller if you're
curious about the api code i'll include
that and more in the github
link that i'm going to put in and don't
mistake the final code or the starter
source code i will have a link to both
today we will add user authorization to
our express api we're going to start
with the code repository from the last
tutorial but if you don't have it you
should be able to easily follow along
without it or you can download or clone
the starter source code from the link
that i've provided in the description
below we're going to get started by
simulating a user's database table in
our model directory with the user's json
file so let's open the model directory
and let's create a new file and name it
users.json
and now we really don't need to put
anything in here but i'll go ahead and
just put in an empty array as if it
would hold json but it's not right now
because we're going to write over this
really now user authorization requires
two routes a registration route to
register a new user account and an
authorization route to authorize the
user after they have created an account
but we're going to start in the
controllers folder because that's where
we'll really
handle the routes and what we do with
them so let's go ahead and create a new
controller and let's call this
register
controller.js
okay in the register controller let's
start out by pulling in our users from
the users database that we're simulating
with that json file i'm going to create
an object and set this up much like you
would see with use state and react where
we have our users state and we'll go
ahead and require those users
and this will be slash model
slash users.json
and after we do that let's go ahead and
have a set user's method for this object
as well and here we can say function
pass in the data
and now oops i'm so used to writing an
arrow function but this will not have an
arrow
and it's users
equals
data there we go
and now that we've got our set users
function and then our users pulled in so
we'll have our users database object
that contains both of these just a quick
way to simulate that so if you're used
to working with react this should look
kind of familiar even though it's not
really the use state and if you're not i
hope you're familiar enough with
javascript to understand how this will
be the users and of course we'll set the
users using our setter function right
here okay now we need some other things
because we're going to work with that
json file so let's go ahead and pull in
fs promises and that's the file system
so we require fs but then after it we
put dot promises
and then we're also going to need path
so let's require
path well there we go
if i can type path we'll get it and now
we're going to need to install a package
called bcrypt and bcrypt will help us
hash and salt the passwords that we come
in so we can securely and safely store
them in our database so let's go ahead
and open a terminal window i press
control back tick here in windows to
open my terminal window you could also
go to the terminal menu and choose new
terminal but here we're going to type
npm i and then b crypt
that's b c r y p t and we'll go ahead
and install this package it shouldn't
take long
and when it's complete we'll check our
package.json file to ensure that it has
been added to our project and it looks
like we're about ready to do that now so
let's see we've got it installed
scroll down here and i'll choose
package.json
and yes bcrypt has been added to our
package json file so go ahead and close
that out now we can require bcrypt
at the top of our file with our other
requirements so set that equal to
require
and
pull in bcrypt
okay now that we've done that let's go
ahead and define our handler for the new
user information that we'll receive at
this register route so i'm going to call
this
handle
new user
and this is going to receive a request
and a response
now what i forgot to do was make this an
async function which we need to do we
can use async await with bcrypt and we
might need it somewhere else too now
when we first pull this information in
the request is going to have a user and
a password so let's destructure that
from the request body so we'll get the
user
and the password
set this equal to the request.body
and now we can say if
there's no user or
there's no password we'll go ahead and
return right here with the response
the status will be 400 which stands for
bad request that's an http status
code and then json will go ahead and
send
a message down
and we'll say message
and now we'll say
username
and password
are
required and i'm noticing that we're not
wrapping lines so i'm going to press alt
z in windows that's what goes ahead and
tells visual studio code to wrap the
lines of code it may be different if
you're on mac or linux
okay so we're sending that response if
we did not receive the user password but
now at this point we'll know we have
received both the user and password and
this is where we would let me go ahead
and put a comment here check for
duplicate
usernames
in the database
and now since we're connecting to our
json file to use as our database just as
a simulation for this tutorial we can go
ahead and check for a duplicate in that
json by doing this let's go ahead and
define duplicate
and then we'll set that equal to
usersdb
dot
users which will pull in the users then
we can use find and now out of each user
let's go ahead and call this person so
it just doesn't get confusing by too
many users or using the word user over
and over this will be person and then
we're seeing if the person
dot
username from our user database
is equal to the user that has been
submitted the username that's been
submitted by the user
and therefore we can see if we get a
result as duplicate and then we can say
if
we have a duplicate
then we want to return a response
it's response dot
send status and it's 409
and that is a conflict is what that http
status code stands for but if not we're
going to start a try catch now so here's
our try and of course we have a
catch afterwards where we would catch an
error
and now let's go ahead and fill out that
error block first so if we get an error
here we're going to send a response with
a status of
500 which would be a server error
we'll have json and let's go ahead and
send a message here as well
and we can just pass in the error dot
message that is received
okay but in our try block now we need to
go ahead and create our new user and of
course also use bcrypt to hash the
password
okay i'll put a comment right here that
we're going to encrypt the password with
the next line and this is where we'll
use bcrypt so let's define our hashed
password and we'll set that equal to a
weight and then use bcrypt
and now oops i'm sorry we don't want
compare we want hash
and inside of that we pass in the
password that we've received from the
user we're going to hash it and here we
determine the salt rounds so it not only
hashes the password but it adds a salt
to it and that really helps protect the
password if your database is somehow
compromised because at that point if a
hacker were able to figure out the hash
they could crack all of the passwords in
the database but adding the individual
salts makes that much more difficult and
unique for each one so here we'll pass
in 10 salt rounds which is essentially
the default or the standard there
and that will add the salt and the hash
all at once and bcrip does a great job
of storing those together at the same
time so that's really all we have to do
there now that we've got the hashed
password we're going to go ahead and
store i'll put store
the new user
and this remember is just going to our
simulated user table that we're using in
the json so here i'm going to say
new user and define the new user object
first
we'll set this equal to username
we'll pass in the user and then we'll
have a
password that we're going to store
and this will be our hashed
password that we've created now let's
set the new data with usersdb dot
set users
and here we'll pass in all of the data
that we have so we're working with
immutable data instead of adding to the
existing array we're going to create
a brand new array and then set all of
that in the database very similar to
using state and react here so this will
be dot dot
users
db dot users and then we'll also add
the new
user all in this new array that we are
setting
as
the new users or the new users data
okay now that we've set that let's go
ahead and write it to our json file
which is our database in this simulation
so we'll have fs
promises
dot
write file
and now i'm going to break this out into
separate lines just so we can see
everything better i'll go ahead and
close our terminal window too so now on
the first line i'm going to use path dot
join and we're going to pull in the
directory name
and after that we need to go up
out of the controllers directory
and then we need to go down into the
model directory
and then we need to go ahead and write
our
users.json file so this will overwrite
any existing file there
and once we've done that we need to
specify the data we're sending and this
will say
json.stringify and we're going to pass
in our usersdb.users
okay from there let's just log to the
console so we can see all of the users
or at least the one user that we add but
we'll log all in case we go ahead and
test it more than once and add more than
one user
log those to the console and then we
need to send a status
and that status is 201 to say the new
user was created and let's go ahead
and send some json again with a success
you could put message here or whatever
you want but we'll put
new user
and then we'll pass in the user so we
get the username
and we'll say created and i needed to
make this
a template literal instead of using
single quotes we need backticks
and that way we can pass the user
straight into the string that we're
sending back
so we've created the new user and
there's the response
and that's basically it except at the
very bottom now we need to use
module dot exports and let's go ahead
and put this
in an object so we can pull in the full
name of the controller when we import it
just like we have previously done with
the employees controller and there were
finished with the register controller so
now let's go to the routes folder and
we'll need to create a new route for
this let's just call this
register.js
and now at the top of register js we
need to pull in express again and this
will be equal to require
express
and then we need to define our router
so we'll set the router equal to
express.router
and then we'll need to go ahead and pull
in the controller so let's call this
register
controller
and we'll set it equal to require
and then we need
to come up out of the routes folder and
then we'll go into the controllers
folder
and from there we'll choose the register
controller
okay now that we've got that let's
define our router or our route that
we're looking for here and it's a post
and this will come in at the slash just
the root
and then we'll pull in our register
controller dot
handle new user
and after that we can use module exports
on it i can spell module
we'll set this equal to
router as we do with our routes and now
finally in the server.js at the very
bottom of our file tree we want to go
ahead and add this route
with the other routes so i'll just copy
this down to start off
we have this slash and this will be app
use and now we're going to use the
register route
and we're going to require routes slash
register and we can save that i'm going
to collapse this open editor so we can
see all the file tree at once and if you
remember in our package json we have
nodemon installed as a dev dependency
and that will help us go ahead and start
this dev server so i'm going to press
ctrl backtick once again to launch a new
terminal window again you can do that
from the terminal menu as well
and now i'm going to type npm
run dev to launch our server and we
should get a message here saying what
port it's running on and it looks like
we've got an error let's see where the
error is
cannot find module
model user json
oh and that's because i named it users
json so we didn't import that correctly
let's go back to our register controller
and change that to users.json
and we'll save and now we should see
nodemon restart and we're running on
port 3500. okay now we can check our
route and i'm going to use an extension
called thunderclient to do that you see
thunderclient up here in the top left if
you don't have thunderclient installed
you can do that or you can use something
else like the app
postman would be a good one that is
normally used to check api routes with
but you can find thunderclient in vs
code extensions and i've found it's
pretty good to check apa api routes with
in a dev environment so here it is on
the left i've got the circle with the
lightning bolt and we can go ahead and
create a new request
and you can also save collections and
i've got a collection here already
with the request that i want to make so
i'll look at this first one and you can
see
it's a post request and it's going to
http colon slash
port 3500
and this goes to auth i don't want the
auth we created the register one let me
find that there's the register one okay
now if i click on
thunder client or the file tree or
anything else it will hide this and i've
noticed that thunder client has a much
better appearance when we hide that bar
over here so now we have the request on
the left we'll get the response on the
right and i can still see the node.js
console down here at the bottom so
with the register route in here and it's
a post route i need to check the body
and make sure i'm sending something i'm
going to send user walt 3 i believe i'll
just create walt 1 this time around
and here's a password
and of course it's unencrypted right now
but we'll see what we get in the node.js
console because remember we're going to
log to the console the users and that
should also include the password if i
remember correctly and of course we'll
get the response over here that the
client would get
so i'll click send
and on the right we've got success new
user walt one created now here at the
bottom it's got just a little more
information so we need to pull this up
so we can see more
and there you see
our user's json and it created wall one
and here is the encrypted password that
is both hashed and salted that bcrypt
helped us create from the password that
we passed in
so now let's go ahead and pull this down
and let's create one more user let's
just create
walt 2
and we'll send this
success new user walt 2 created
and now let's once again look at the
node console and you can see now both
users were logged to the console here in
node and they have different passwords
as well that are encrypted
and of course we could break all that
down but i'll let you look at the link
to be crypt and i've got another good
article on that as well i'll link to
both of those in the description below
so make sure you do that okay we have
tested and handled the registration but
of course now we need to go ahead and
handle the authorization as well i'm
going to close the terminal again
and i'm going to copy the import of
usersdb with users in set users
and i'll go ahead and create an auth
controller now in the controllers
directory so we'll call this
authcontroller.js
and now the first thing i'll do is paste
in the user's db where we import in our
users
json that simulates our users database
table and we're also going to need
bcrypt again so let's set this equal to
require and
be crypt
and now we can begin constructing our
function let's call this handle
login
and let's set it equal to an async
function that has a request and a
response
now inside the function will start out
just like we did inside the register
controller where we expect to get a user
and a password parameter both sent to us
and if we don't we'll send back that
400 http status code that says it is a
bad request so we can paste those right
in i'll press alt z again so the code
wraps
so they start out the same way but after
that there are definitely some
differences and what we need to do now
is try to find the user that's sent in
before we even concern ourselves with
the password let's just see if the
username exists so i'm going to call
this found user
and set it equal to usersdb.users.find
and then we'll look at each person in
the users table and we'll say
person.username
equal
to user and find works this way until it
actually finds somebody and then if it
finds somebody it will return that value
and if not of course it will be false or
undefined at that point so
now we could log this and take a look
but let's just say if
we do not
find a user if we do not have a found
user
we'll go ahead and return the response
dot
send status
and we're going to send a 401 which
means unauthorized
and i could put a link to mdn that lists
all of the http status codes in the
description below as well i think i will
do that so there if we do not find the
user we know hey that's it there's no
need to look any further this is
unauthorized and so it won't work
but now if we do find a user we need to
go ahead and let me put a note here just
say
evaluate
now if i could spell evaluate there we
go evaluate password
and we'll use b to do that so let's
say match
is what we're going to define and we can
just say await
bcrypt dot
compare
let's pass in the password that we
receive with the request and we're going
to compare it to the user that we've
found
and the password that exists for that
user and now if we do have a match
if the passwords match we can reply
with the json and once again we could
send a success message
and we'll just say oh back ticks again
and we'll say
user and pass in the user value
and from there we'll say is
logged in
but we could also have an else there
and
if we do not have a match at this point
then we'll say response send status and
once again this will be a 401
unauthorized
and we can save that and we're
essentially finished with the function
except for
adding the module.exports here once
again
and we'll put it in an object again and
we'll say handle login
but the other note that i want to add to
this and we're not doing it in this
tutorial but we will in the next
is this is where we would create
a jwt to send
to use with the other routes that we
want protected in our api
so we'd actually create and send a
couple of jwts which would be a normal
token and then the refresh token so
let's put create
jwts that would be right here in this
spot and we'll just leave this note here
for the next tutorial but now we've
created our handle login
in our auth controller so we once again
need to create
here we go scrolling the tree we need to
create a route for auth
so we'll just put auth.js right here
and we can copy
most everything we have in the register
and just change this over in auth so at
the top we need express and router just
the same but instead of register this is
the auth controller
we can select all of those with control
d and change them to auth
and after that instead of handle new
user
it is handle
login that we want from the auth
controller and save and our auth
route is complete now let's go back to
the server once again
and let's add the auth route as well so
let's just copy down i press shift alt
and the down arrow to copy a line down
in windows
and i'm going to put in the auth route
here i'll press ctrl d to select both
and just put off
and save that as well with that complete
let's go back to thunder client
and we'll look at our auth collection
there it is
and let's see if we can pull up the
correct route this is the auth route
that's great so i'll hide the rest and
we get a better look here i want to see
that terminal again so i'm going to
press control and back tick to pull up
the node terminal and here we are in
port 3500 so we're sending another post
this is going to localhost port 3500 and
the auth route right here so let's look
at what we're sending in the body
and we want to log in with walt
1
and the password that we had previously
submitted as well so we'll see the
client what the client receives here on
the right
and we'll see if anything is logged i
can't remember if we logged anything to
the console this time around let's go
ahead and send this
well we at least log what route we hit
here in the console but now this says
user wall 1 is logged in
let's go ahead and try to log in with
the user we don't have like walt 3
and now we get our 401 unauthorized
which is exactly what we wanted and i'm
just thinking now earlier
we didn't send something to register
that already existed so let's try to
register a name that we've already
registered we registered walt 2 last
time this is going to the register route
so now let's try to register
and we got a 409 conflict that's exactly
what we expected there as well so it
appears our routes are working correctly
and everything seems good so this is
handling
authorization with username and a
password on the back end
and we have simulated a database by
using
users.json but in the future we could
connect this to mongodb
mysql or some type of database
technology and you really wouldn't use
just a json file in production or at
least i wouldn't recommend it but we
will come back in the next tutorial and
learn how to protect routes with json
web tokens
today we will add protected routes to
our express api by using jwts let's
discuss the jwt strategy we're going to
implement today so what are jwts what is
the acronym jwt and abbreviation for
well i'm very excited about the upcoming
launch of the james webb telescope but
in this case that is not the correct
answer concerning our node and express
rest api jwt is an abbreviation for json
web tokens jwts can be considered to be
a form of user identification that is
issued after the initial user
authentication takes place
when a user completes their login
process and they are authenticated our
rest api will issue the client
application an access token and a
refresh token the access token is given
a short time before it expires for
example 5 to 15 minutes and the refresh
token is given a longer duration before
it expires possibly several hours a day
or even days while no security measures
are perfect we do want to consider the
risks of cross-site scripting and
cross-site request forgery i will
provide links about both in the
description below our api will send and
receive access tokens as json data to
avoid the previously mentioned risks it
is recommended for front-end client
applications to only store access tokens
in memory so they will be automatically
lost when the app is closed they should
not be stored in local storage or in a
cookie essentially if you can store it
somewhere with javascript a hacker can
also retrieve it with javascript just
keep access tokens in memory which you
might also refer to as the current
application state our api will issue
refresh tokens in an http only cookie
this type of cookie is not accessible
with javascript refresh tokens do need
to have an expiration which will then
require users to log in again refresh
tokens should not have the ability to
issue new refresh tokens because that
essentially grants indefinite access if
a refresh token falls into the wrong
hands so the overall access token
process involves issuing an access token
during user authorization the user's
application can then access our rest
api's protected routes with the access
token until it expires
our api will verify the access token
with middleware every time the access
token is used to make a request
when the access token does expire the
user's application will need to send
their refresh token to our api's refresh
endpoint to get a new access token of
course the refresh token is also issued
during user authorization our rest api's
refresh endpoint will verify the token
and cross-reference the refresh token in
our database too storing a reference to
the refresh token in the database will
allow refresh tokens to be terminated
early if the user decides to log out and
again refresh tokens need to be allowed
to expire so indefinite access cannot be
gained we're going to start with the
code repository from the last tutorial
but if you don't have it you should be
able to easily follow along without it
or you can download or clone the starter
source code from the link that i've
provided in the description below let's
get started today by installing the node
packages that we're going to need so i'm
going to press ctrl and the back tick
you could also go to the terminal menu
and open a new terminal window from
there i'm going to type npm i and then
we can add all of the packages that we
need all on one line so the first one is
dot env
the next one is json web token
no spaces there and then the last one is
cookie
dash
parser and we'll press enter and all of
those packages will install and then
we'll check our package json just to
make sure the dependencies have all been
added and that was fairly quick let's go
ahead and look at the package.json
and we can see under our dependencies
we've got the cookie parser we've got
dot env
and we've got the json web token package
with that complete i'm going to close
the terminal window and over in the file
tree i'm going to create a new file at
the root level and i'm going to name
this dot env just like that and now we
can put our environment variables in
here i'm going to use all caps and type
access
underscore
token underscore secret
and set that to an equals and then we'll
get the value in a second and then i'm
just going to copy that down and change
the word access
to refresh because we're going to create
an access token and a refresh token so
let's save this much for now open the
terminal window back up and now we can
type node in the terminal to go into
node and now we're running node at the
command line interface node has a common
core module called crypto so right here
at the command line we can type require
and now we can put
crypto in here and from there we can add
random bytes with camelcase with a
capital b
the number 64
and then dot to string and we'll say hex
and this will give us
a
random crypto bytes string that we can
use as our access token secret so i'll
press enter
and now i'm going to copy this and we
don't need the quotes at all inside of
our dot enb now ctrl c to copy and i'll
just paste this in here as our access
token secret now i can press alt z in
visual studio code to get that to wrap
so it doesn't go
beyond the edge of the screen so that is
our full crypto line that we just
created there now let's go ahead and
create one more i can press the up arrow
in the command line just to pull that
command back up press enter and it
generates another
random crypto line here that we can
copy and paste right into our dot env
file and with that i'm going to press
control s to save and close the terminal
actually i'm going to open the terminal
back up real quick because we didn't
exit node we press control c and then it
says press control c again so we press
it again and now we have exited node at
the command line now i'll close the
terminal window one other thing we want
to do with the dot env file is make sure
it has been added to our get ignore file
you do not want to send your environment
variable values that you're storing in
the dot env file or you could just say
you do not want to send your env file to
github you want to
keep this in your dev environment and
then when you host somewhere whichever
host you have they should have a way to
put the environment variables into their
hosting service then you can pull those
out now we need to go to our controllers
folder and open up our auth controller
that we've created in the last tutorial
and we left a note for ourselves that
this is where we want to create the jwts
on line 15. before we get started on
line 15 let's go ahead and pull in
everything we will require today so
let's define jwt
set that equal to require and then pull
in our json web token package
after that we need to go ahead and
require
dot
env
and then we need dot config after that
and after that we need our
fs promises because we have not
integrated or any other database
technology yet so we are still working
with simple files and json values so we
need this fs promises and we'll set that
equal to
the fs module
and after that we need dot promises
and then finally we need the path module
we'll set that equal to path
now let's scroll down so we have just a
little more room to work on the screen
and we're back here and now line 15
turned into line 20 where we're creating
our jwts and let's define our access
token
we use camelcase with a capital t on
token and we'll set this equal to jwt
dot
sign
the first thing we need to pass into the
jwt
is a payload so what we're going to use
is our username object you don't want to
pass in anything like a password or
anything that would
otherwise hurt your security because
this is available to all if they get a
hold of your token so what we want to
pass in is just the username i'm going
to do this on a separate line and so
we'll create this object
with username
and here above we had a found user and
then we can say
username now this is coming from up here
where we found the user that was logging
in after that the second thing we need
to create our access token is our secret
that we defined in the dot env file so
we access this by process
dot env dot
access
token
underscore
secret
and then finally what we need is an
options value here and we can say when
this token expires
and this is camelcase expires in and
we're going to make this very short for
this tutorial that will allow me to show
the token expiring and see what happens
but what you might want to do in
production would be maybe 5 minutes or
15 minutes some small window of time but
30 seconds might be too short it is all
a preference though and with that
defined let's go ahead and highlight all
of this and then i'll just press shift
alt and the down arrow to copy it all
down and we'll just make a few changes
to go ahead and create our refresh token
as well so i'm going to type
refresh here instead of access we're
once again going to pass in the username
but then we need the refresh token
secret
now a refresh token needs to last much
longer than the access token but you do
want it to expire at some point and so
i'm going to make this
one day and i'm going to give a link to
the package the json web token package
on npm js and there you can see
another link and more documentation on
the different values you could put in
here but one day is what we're going for
and then we want it to expire and we
want our users to have to log back in
you could make an indefinite refresh
token but that means if someone gets a
hold of it and they're not the correct
person they will always have access and
you do not want that with both tokens
created i'm going to scroll up for a
little more room again and we want to
save our refresh token in the database
which will also allow us to create a
logout route in the future that will
allow us to invalidate the refresh token
when a user logs out so let's start by
doing that and once again we're just
working with our json files for now and
we haven't connected this to an actual
database that we will do in future
tutorials but of course that lets us
modulize these tutorials and then we can
attack say or go over to postgres
or anything we want to in separate
tutorials so for now just the json file
and i'm going to define other
users we had a found user
so now we'll set other users equal to
the users
db dot users
dot filter
and now for each person that we have
we're going to say person dot
username
not equal to
and now let's have that not equal to i
believe it was
the user that we came in with the
request right here however we also have
our found user dot user name and i
believe i'd like to use that since we
have found it so let's say found
user
dot username
and now that creates
an array of the other users that are not
the username that is logged in and now
let's create a current user
we're going to set this current user
equal to the found user
and then we're also going to add in the
refresh token
that will be saved with this current
user and now let's set our users once
again and we can say users db dot
set users
and in this new array
we'll go ahead and
add the other users
and then we'll add the current user that
we just created i really don't know why
i'm putting spaces there really don't
usually do that in an array and okay
let's go ahead and put the semicolon and
let's write our new users file this
would be await and then fs
promises
dot write file
i'll go down to a separate line i'm
going to scroll up once again just to
put that in the middle say path dot join
we'll pull in
the directory name
then we need to go up one directory
and then we need to go into the model
directory
and then we need to go ahead and name
this users.json as our users file is
after that what we want to put in we
need to json stringify and pass in
usersdb
dot
users which will write our new users
file and so i should have put a little
comment here at the top this is
saving
refresh
token with
current user which again doing this will
allow us to invalidate that refresh
token as the current user logs out if
they log out before their one day has
expired so as a quick review we have
created the jwts once we have authorized
the login which was with bcrypt in the
previous tutorial
but now that we authorize the login we
create the jwts we have a refresh token
that's been saved
into the online database and an access
token and yet we still need to send both
the refresh token
and the access token to the user the
easy part of this is to just remove what
we were sending in our json and instead
send the access token we can send that
as json and on the front end the front
end developer or as a full stack
developer if you're developing the back
end and the front end you really want to
store this access token in memory
it's not secure in local storage and any
cookie that you can access with a
javascript could also be accessed so
anything that really that javascript can
access where you would store it it's not
really that secure so if you store the
access token in memory which has a very
short life span we only gave it 30
seconds here but like i said maybe you'd
give it 15 minutes by keeping that in
memory
you're not storing it anywhere
vulnerable after that the refresh token
well what we're going to do i've
definitely seen tutorials that also just
send it along in the json but that kind
of creates a dilemma for the front end
developer or as you develop the front
end because you do need to store the
refresh token we want to send it as a
cookie and i know i said a cookie could
be vulnerable to javascript but if we
set the cookie as http only it is not
available to javascript so that's what
we want to do so let's say res.cookie
and now here we can name the cookie i'll
just name it jwt
from there we want to pass in our
refresh token
and after that we want to give options
and the first option i'll give a space
here http only
set that to true
and after that we want to set a max age
here we go and now i'm going to set this
to 24 which would be 24 hours
60 times 60 times
1000 and this is in milliseconds here so
what we're really getting
is one day because that's what that is
equal to as we set the max age of the
cookie and i'll go ahead and cap that
off here you could set it for longer as
well now cookie is always sent with
every request but the nice thing about
an http only cookie is that it is not
available to javascript and while it's
not a hundred percent secure it's much
more secure than storing your refresh
token in local storage or in another
cookie that is available to javascript
so let's save this and now we know we
are sending our refresh cookie that is
http only to the user we're also sending
the access token
oh here we're sending our refresh token
is what i meant to say in a cookie that
is http only
we're also sending the access token as
json that the front end developer can
grab
and we're also storing this same refresh
token in a database here that can be
cross-referenced when it is sent back to
create another access token with our
auth controller complete let's now go
look at the middleware folder
and we need to create a new middleware
file here and let's call this verify jwt
dot js and we're going to need jwt and
dot env once again in this file so i'm
back in the auth controller just long
enough to copy these two and then go
back to the verify jwt dot js
and i'll paste in our required packages
right at the top
after that i'm going to create verify
jwt
set this equal to a function that has a
request
response and next as middleware should
and now let's define the auth header at
the very top and this is going to be
equal to request
dot headers
oh and then we need a bracket not a dot
and then
authorization
and of course we need to check to see if
that is actually received so now we can
say if
no auth header
we're going to send a response which we
need to return
and then just send
the status 401 which stands for
unauthorized
after that i'm going to go ahead and put
a console log statement to just go ahead
and log the auth header for when we test
this we'll be able to see that in the
console you'll see a line that looks
like this and it will say bearer
and there will be a space and then there
will be
the token after that so we can define
our token now
and we can set this equal to
the auth header and then we can call
split on that space that i just
described and we'll put the space in
here on the split and then we need the
token so it wouldn't be in the zero
position it's in the one position after
that
and now we can verify the token with the
jwt dot verify
and here we'll pass in
the token first
after the token
we'll pull back in our secret and this
will be the access token secret because
that's what we'll verify with this
middleware
and after the access token secret there
will be a callback that gets an error
and then we'll call this you could call
it token if you want we'll call it
decoded
because it will have decoded information
from the jwt
and now we'll say if we have an error
let's go ahead and return
and send a status once again
this status will be 403 forbidden
because at this point we'll know we
received a token
but something about it wasn't right in
other words it may have been tampered
with and so
this means we have a invalid token
so we'll send a 403 instead of a 401
which means you are forbidden from
accessing this
and then we'll set the user
equal to
decoded dot username remember we passed
in the username to the jwt so now it's
been decoded and we can read that
and then we'll call
next from our middleware
and that pretty much wraps up that whole
middleware function right there except
we need to remember to export it so
we'll say module.exports
set that equal to verify
jwt and save with the middleware now
complete we can add it to a route or
routes that we want to protect
so let's open up routes and api and go
to our employees js file that has all of
the employee routes in it and we need to
import that middleware here so we'll say
const
verify jwt
equals
require
and now we need to go up out of the api
folder
up out of the next folder the routes
folder and then go to middleware and
then choose
verify jwt
and once we have that we can just put it
let's say in our git route if we only
wanted it in the get route
before
we call the controller and so it will go
through the middleware first and then go
to the employees controller here so
let's save this and now let's test this
with thunderclient so over here to the
left i'll open up thunderclient
and we're going to need to log in first
so i'll go to our collections and i have
an auth collection that i created and i
want to go to the auth route
i'll click this and now i'm going to
click over here again to hide the tree
so we can see better and i'm also going
to press control and backtick to open
our terminal window we haven't started
the server yet so that's something else
we need so we need to type npm
run dev to go ahead and start our server
and it's running on port 3500 and so you
can see the route we're going to go to
is a post route it's localhost 3500
auth that we had previously set up to
log in with the password so in the body
we're sending wall 1 and password
and the password that we had in there
previously and this is in the users file
that i have if you haven't done this you
need to go ahead and either create this
in the file or use the register route
that we previously created and create a
new user and then this will work but
we're right now we're going to log in
with user walt 1
and this password using this route so
i'll go ahead and send
and you can see over here on the right
the response we get is a 200 and we get
this access token this access token will
only be good for 30 seconds though so
let's see if i'm fast enough to come
back here to our collections
open the employees api
and now let's look at this and i need to
go ahead and in auth i need to send a
new
access token now we can send
and we got our employees back but this
will expire and notice down here we also
got the console log statement i put in
here's bear
and then the space and then the token so
now if i send this again the token
should have expired
and now we get the 403 forbidden because
the access token that we sent was only
good for 30 seconds now let's look at
the employees.js file again and i will
go ahead and click the file tree so we
can see our files also we're only
applying this verify jwt middleware to
the get route right now
and this is a good way to do this if you
have select routes that you want to
protect or verify but not all of them
however if you know you want to protect
all of the routes in your api
there is an easier way to do this so
let's go ahead and remove this and we'll
pull this out of here as well
and i don't think we'll just retype it
in the server file because that's where
we're headed next so we've removed the
verify jwt from the employees js file
all together let's go to server js our
main file
and here
we can just pull this middleware in
so i'm going to do it right underneath
the error handler that we had middleware
for so we'll say
verify jwt
set this equal to require
and here we want to go into we don't
need to go up at all actually we just
want to go into dot slash
middleware slash
verify jwt
and now that we have that we can use
this just like we've used other
middleware in our file but we probably
don't want to use this verified jwt
middleware for all the routes probably
not the root route that we have here
definitely not to register a new user or
we'd never be able to register in the
first place and not even to auth because
they have to visit auth first to get the
token but we do want to put it before
the employees route so i'll put an extra
line there
and then
right here we'll say use
verify
jwt
and if you remember
this works like a waterfall so in this
order everything after this line will
use the verified jwt middleware so
any route we do not want to have
verified with the jwt we need to have
above this line right here so let's go
ahead and save this once again
and now we can go back
and let's log in again and get another
well we close up the api there there we
go here's our offline let's log in and
get another 30 second access token
here we are
okay now let's go back to the employees
and once again we can just use the get
route before it was forbidden because it
expired but now let's go to the auth tab
here in thunder client
select all delete that paste in the new
token and submit
and we've got our employees list once
again but if i continue to submit
it will expire
as soon as the 30 seconds is up and
there is our 403 and just to show that
this is on all routes now let's go back
to our employees api to the post route
for posting an employee
here in the body
we've got john doe so that looks like we
could go ahead and post to the employees
for john doe let's see if we can send
this
and we get unauthorized so our verify
jwt middleware is working on all routes
okay let's go back to the file tree we
can close the terminal once again we can
close out of thunderclient for now and
let's close up the middleware what we do
want is to go back to the controllers
and we're going to create a new
controller here called
refresh
token
controller.js because we're going to
have a refresh token route and before we
get started on our refresh token
controller because we're going to need
it let's go back to the server.js
quickly
and at the top of the server.js we
pulled in the verified jwt
we also need to go ahead and define
cookie parser the other package that we
had required at the beginning of the
tutorial
and let's require
cookie dash parser there we go but we'll
define it with cookie parser capital p
camel case right here so i can go ahead
and copy that
now much like where we used the
express.json and express.url
encoded that we use for form data and of
course
express.json is for json data we just
want to add
middleware
or cookies right here so we'll say
app.use
and now we'll pass in
cookie parser and we want to call that
into action right there
and save our server.js file now we're
ready to go right back to that refresh
token controller and we'll begin by
copying the auth controller
control c i'll just copy everything and
paste it in but we will make some
modifications
so we need the user's db we will not
need bcrypt because we are not doing
user password authentication there we do
need jwt
and dot env
but we won't need fs promises or the
path in this either so we can remove
those dependencies now instead of handle
login let's call this handle
refresh
token
and this does not need to be async so
we'll just have the request and response
here
after that we won't be looking for a
password
but we will be looking for a cookie so
let's say const
cookies and set this equal to request
dot cookies and so we will have a
similar if statement here at the
beginning
but we'll be checking for a couple of
things one is to make sure that we have
cookies but now let's use optional
chaining with a question mark and a dot
the optional chaining operator and put
jwt after it so we're checking to see
if we have cookies and then if we do
have the cookies we're also checking to
see if there is a jwt property and this
is saying of course if these do not
exist
then we're going to return something
here and we're going to return a 401 and
we do not need a json message after that
so i'll just eliminate that portion of
it
and save this it looks like i may need
to go ahead and press alt z to wrap any
lines that exceed
the edge of the screen so there we've
got a 401 if the cookies do not exist
and
maybe we have cookies but no jwt either
way that gets a 401 which is
unauthorized now let's go ahead after
we've confirmed that at least and let's
log to the console
cookies.jw
so when we run this you'll be able to
see what values we get from that and
likewise let's go ahead and define the
refresh token now that we've received
now that we know it exists within the
cookie and we'll set that equal to
cookies.jwt
and once again we do want to find a user
but now we're receiving a refresh token
and not a username or password for this
so we'll say person and there is a
refresh token that should be saved with
each user if they have created one
and so here we'll just match that to the
refresh token
that we have now defined here so this
would be our found user then if that
user exists and once again we can say if
we do not find a user let's now send the
status forbidden here and i'll just
highlight that
okay we're not evaluating a password
here we are evaluating a jwt though so
these lines will change as well so we
can get rid of the const match line
and instead well we can even get rid of
the if match here as well
and instead of that we'll type
jwt dot verify
and now we'll pass in
the refresh token that was received
now we'll say process dot env
dot
refresh token secret
and then we'll have
an anonymous function that once again
has error and decoded its callback
function
and before i type the rest i think i'm
just going to get rid of this because
it doesn't need to be there at this
point either so let's just go ahead and
clear it out now
and with that cleared out we can finish
our function here for the token
verification and
if we do not have a valid token we can
say if we have an error
or
we can also check the found user dot
username because we did have the
username encoded in the token
so if it's not equal to the decoded
dot username in other words maybe it was
tampered with again
then we can
return a response with a send status
and say 403 once again
otherwise everything is good and we're
ready to create a new access token
descend because the refresh token has
verified so we'll set this access token
equal to jwt dot sign
and then inside of the access token will
once again have a username
and this username will be the
dot username because it should be the
same username that was verified before
and after that we can say process dot
env
dot
access token secret
if we can find that access
token
secret there we go
and now we need to put an expires in
once again
let's put expires in
and let's set this to 30 seconds as well
however remember you might want to set
this longer in your production app this
is just for the demonstration of this
tutorial so i can show it expire and
then of course after we've created this
access token we need to go ahead and
send the access token
so we'll send this json down and it'll
say access
token
and once we've created that much all we
need to do is export it
so now instead of handle login we once
again have handle i believe it was
refresh token yep
and we should be able to save that
and i don't know why i put a semicolon
there it's not really needed so
once we have saved that we should be
good to go with our refresh token
controller and we can create the refresh
token route okay in our routes file now
we can open that up and we can look at
our auth route i'll just highlight all
and copy and we'll create a new route
called refresh
dot js
i'll go ahead and paste that in but
instead of the auth controller we need
the refresh token controller so we'll
spell all of that out
and instead of handle login we need
handle
refresh token and before we're finished
with our refresh route let's change the
http method from post
to git as well so this is now a get
route for refresh okay now we need to
insert this route into the server along
with the other routes so we'll go to the
server.js file and you can see we've got
root register auth and of course we want
this to also be before we verify the jwt
because the refresh route actually
issues a new access token and that's
what verified jwt verifies so what we
want to do now
is just copy a line down i pressed shift
alt and the down arrow in windows to do
that and i'm going to change off
to refresh
and now the refresh endpoint will
receive the cookie that has the refresh
token and then that will issue a new
access token once the access token has
expired so we'll save this and we should
be ready to try it out so i need to open
up the terminal window again and we can
see our server is running on port 3500
now let's open up thunder client
and we're ready to check out the refresh
route but first we need to go to the
auth route
and here it looks like the off path
i'll hide the tree over there and so now
we're going to localhost 3500 auth we're
once again going to authorize
walt here with his password so let's
send this
and we got a new access token but what
we also get from our auth route is a
cookie and this cookie has a refresh
token and here's the jwt name for the
cookie and here's the refresh token now
a cookie is sent every time we don't
need to paste it in in an auth area like
we did with our access token
the jwt cookie is sent every time we
make a request to the domain that it's
associated with so we'll go back over
here to thunder client we'll come down
to our refresh route and i'll hide that
tree now we're going to localhost 3500
refresh and you can see we don't need to
post anything in the body we don't need
to post anything in the auth because we
have the cookie that we're going to send
so let's send this cookie
and now we got a new access token and
you can see we also logged our
information as we set it up in our path
to log
the refresh token that we received from
the cookie down here so
we've got
a new access token and our refresh token
is used to do that so now every time we
hit this refresh route and we send our
cookie with the refresh token we should
get a new access token and you can see
every time we're getting a different
access token over there so that is what
refreshes our access and that's why it's
called a refresh token and so now we
have a new
limited time access token to use again
in production you might want to set it
to 5 minutes or 15 minutes instead of 30
seconds or something like that
but then our refresh token has the
longer duration that we store in an http
only cookie and that's because we don't
want it to be available via javascript
so it increases our security nothing's
100 perfect but it's the best way to go
i believe for that so we're sending the
http only cookie that has the refresh
token and that issues us the new access
token now that we've finished testing
the refresh route one extra measure of
security or one extra step we can take
is to offer a log out route and with the
log out route we could actually then
delete the refresh token and not let it
last for the full duration and that just
gives our users the opportunity to log
out and of course delete any existing
tokens and of course the access token
should also be erased on the front end
when the login or log out
link or button is clicked as well of
course we're working on the back end so
we won't be doing that today but let's
go ahead and add this new controller for
the logout so we'll have logout
controller.js
and now that we have that let's go ahead
and copy our refresh token controller
and we will just make some modifications
to handle the logout because we need the
same requirements at the top we've
pulling in the users so we can check our
database we've got the jwt requirement
and we've also got dot env i'm going to
close the terminal window
and now i said we had the same
requirements we don't quite have the
same requirements i'm sorry we don't
need the jwt or the dot emv
what we do need
is the fs promises that goes with
accessing our database because we're
just using a json file so we'll require
fs
dot
promises and we once again need path so
we'll require
the path
and again these are only to access the
current json file we're using for our
users database this is a mock database
this will be replaced of course
with or postgres or whichever
database technology we work on in a
tutorial in the future
so we've got this
down for the requirements and let's move
forward with the log out so instead of
handle refresh token now we're going to
handle the logout
and we'll have a request and response
and then let's just add a note here for
the front end if you're doing full stack
development like on
client
also
delete
the access
token
we can't do that here on the back end
you need to do that
in the memory of the client application
just
zero it out or set it to blank or
whatever when that log out button is
clicked what we can do though is take
care of the refresh token so here once
again we need to get the cookies as they
come in now let's erase our logs here we
were doing for the cookies and the
cookies jwt before if you wanted to see
those in the console
and we'll once again verify that we have
got cookies and then that we also have a
jwt in the cookies and if we don't
well that's okay because we were just
going to erase it anyway so let's send a
status 204 that means it's successful
and there's essentially
no
content to send back that's what that
status is so you've had a successful
request and we're not sending any
content
and this needs to be send status instead
of just status there we go response.send
status got that now let's go ahead and
define our refresh token and we'll do
that in the same way setting it equal to
cookies.jwt
now let's see if we have that refresh
token in the database so that's what
we're checking here
is
refresh token in db
so once again
we try to find a user that has this
token so setting this equal to the
refresh token just as we did
in the previous refresh token controller
is exactly what we need
and if we don't have a found user at
this point we can go ahead and clear the
cookie so we're going to do something
just a little different here and it
won't be forbidden either so we'll get
rid of that
and
let's go ahead and look at how we clear
the cookie first so if we don't have a
found user but we did have a cookie to
get to this point we can just go ahead
and erase the cookie that was sent
and that's going to be with response dot
clear
cookie
and then inside there we'll look for the
jwt cookie
and then you have to pass in the same
options that it was set with so once
again we'll say http only
set that to true
and that should be all we need to do
there except for sending the proper
status so once again we're going to
return
and send the status not a 403 but of 204
which once again means this was
successful but no content okay i'm going
to scroll up just a little bit and now
we can pretty much erase everything here
and we're going to create some new
content
below because if we've reached this
point that means
we did find the same refresh token in
the database so now we need to
delete the refresh
token in the database right here and
we're using file system of course for
that instead of or postgres at
this point but we will change that in
the future right now
let's define other users which would be
all of the other users that are not the
user that we found and set that equal to
users db
dot users
dot filter and we'll just filter this
out we'll pass in a person
and now the person dot
refresh token
is not equal to our
found user dot
refresh token as well there we go
okay so i'm going to press alt z to wrap
that line of code so we can see all of
it and that's all the other users so now
our current user
[Music]
is going to be equal to our found user
and then we're going to set the refresh
token to blank so
we'll leave the refresh token property
but now we're just erasing it from the
user and now we can update our users db
with our set users
and we'll go ahead and pass in our
other users
and then we'll pass in our found user
oh and i'm sorry that's not found user
that should be the current user that we
just defined there we go so found user
was before we erased the fresh token now
the current user has the updated refresh
token that is zeroed out or blank if you
will now let's just write this to the
file so we'll await fs
promises dot
write file
and now on a separate line here i'm
going to say path dot join
pass in the directory name
after that we'll need to come up one
folder
and then we'll say in the model folder
and then we'll name the
users.json file
okay then we need to go ahead and use
json.stringify
and write in the usersdb
dot users
and that should update our users file
and so after that we need to go ahead
and delete the cookie which we saw how
to do before as well so we use clear
cookie
and we reference a jwt
and we need to pass in the same options
as well
http only set to true now i will add in
production both when we send the cookie
and when we delete the cookie you also
want to add the flag
secure
true and that will make it only serve
so it only serves
on
https we're just using a dev server that
uses http but you want a secure
connection with
https so
we don't add this in development but we
would in production the option of secure
and set that to true
okay and after that we want to send our
status again which once again is a 204
all as well but we have no content to
send back
and after that we need to change our
export from handle refresh token
to handle logout and save okay with the
controller complete let's create our
route file
and we can just go here to the refresh
file we created
select all and copy
and now i'm going to create a new file
in the routes called logout.js
and now i'll just paste everything in
from the refresh
route and we'll change that so we need
express we need the router but instead
of refresh token controller we'll select
all three of those with control d
and i'll just type
log out
and this will be a get route again and
the export to router is correct but we
do not have handle refresh token here we
also have handle
logout as
the function for the controller and with
that complete now we can go to the
server and add that route as well we
won't need to verify a jwt to log out so
i will just once again shift alt and the
down arrow to copy a line down
and i'll change refresh
to log out and save now let's open a
terminal window again oh and we've got
an error so let's see what our error is
here in node
a weight is only valid in an async
function ah we did not do that in our
controller let's go back to the logout
controller and we used await when we
used fs promises so let's make the
handle logout an async function
and save and let's see if node restarts
and yes the server is running on 3500
all looks good there so now we can go to
thunder client
and we'll hit our auth route to make
sure everything is logged in we're going
to log in our user walt one
and we've got an access token
and we've got a cookie
so now let's go back to thunder client
and i have a log out route that is a get
route as well so we have get localhost
3500
log out you don't need to pass anything
in the body or the auth when we log out
but let's go ahead and do that
and we hit log out and everything seems
to be fine
we can see our cookie has now been
erased
so now if we go to any of the other
routes
like refresh that would use our refresh
token cookie
we don't have a cookie to refresh with
so now let's try localhost 3500 refresh
and it's a git route and see what we get
well we can see in the console we logged
or attempted to log a cookie but it
didn't exist here and we're not getting
a response so let's look at that i can
cancel this here in thunder client let's
go ahead and look at our
refresh controller
and see what's going on there so refresh
token controller
and here is where we're logging the
cookies we can get rid of these lines
actually now so we don't need those
but let's see if we can figure out what
is going on and yes here's the error i
said response dot status 401 it needs to
be response dot
send status 401
just like we had down here with send
status 403
so if i've typoed any of those others
throughout this code it looks like i had
send status everywhere else but just in
case you see that to actually send the
status it needs to be send status
instead of just status status is
chainable if we're sending a response
after that
but now this should work so let's once
again go back to thunderclient
and we'll hit our refresh token route
although we don't have a cookie
and we'll see what happens
ah we immediately got our 401
unauthorized because now it sends the
status so that's correct let's go back
and log in again with our auth route
localhost 3500 auth send that we have an
access token
we have a cookie and everything seems
good so all routes are working as
expected okay i thought i was at the end
of the tutorial but after playing around
with some front end code there were a
few things i thought i needed to go
ahead and clear up or include one is
here this is front end code by the way
this is not what we were working on in
express if you use fetch to access
what we had set up say our auth route
well you're going to need to include
a credentials option
again on the front end to have fetch
send the cookie now if you use axios i
believe there is a with credentials flag
that needs to be set as well
and we could do that in a future
tutorial but right now
just looking at credentials for use with
fetch so you have to set that to include
but that sets off a chain of events so
even though this is required here to
send the cookie
then what happens
is i'll pull up chrome here
we get a course error
and you will be blocked by cores because
the value of access control allow
credentials in the header
of the response
is blank
and it needs to be set to true
so cores will block this
if you don't have that set before you
reach the course check now what is
happening is a pre-flight options check
it's an options http method request but
then you also need to have the same
access control allow credentials
set to true when you do send the cookie
back as well because that's what's
expected with fetch
so there's a way to fix that of course
and now let's look at some of our back
end code
and let's start in the config folder
and i went to coors options first
and we did have what was called a white
list in here which
that's traditionally what it's called
and i've seen that in articles as well i
don't like the name so much either
just maybe in modern day it sounds a
little racist or something so we've
switched that to allowed origins which
is what it is it's a list of allowed
origins and i put it in its own file
because we're going to use it in
middleware also but i only want to
update it in one place so we have our
allowed origins
formerly called the white list in the
tutorial
and we're exporting allowed origins so
we import that into our cores options
and use it here but then we also create
middleware called credentials
and now this credentials middleware of
course has a request response and next
and we import allowed origins here also
all we're really doing is to say if
the origin that is sending the request
is in our allowed origins list so it's
just a little bit of extra security if
that is the case go ahead and set this
header on the response because that's
what cores is looking for
access control allow credentials and we
set that to true
so now that we have the middleware
created we go to the server file and we
pull that middleware in
and then you want to use the middleware
we're using credentials right here and
of course i'm importing it right up here
above
you want to use that before cores
because of course sees that that
response header is not set it throws
that error
so
that will fix that issue and i would
hope that would be the end of the issues
but no there was one other issue
and let's take a look at what that was
i'm going to open chrome again and i had
to do a screen capture now i was in the
dev tools in the network tab and i was
looking at the auth request and then i
could pull up the cookie
and then there was this little triangle
and the reason i had to do this screen
capture is because i had to mouse over
the triangle
for this message to pop up in this very
small print i'm zoomed in
quite a bit already just to see this and
it said the set cookie header didn't
specify a same site attribute so that
defaulted to same site equals lacks and
then it was blocked because it came from
a cross site response which means
our
front end application was not on the
same domain as our back end api and
that's often the case
so
there's nothing wrong with that it's
just that's why they blocked it says the
set cookie had to have been set with
same site equals none
to enable cross site usage so that tells
us something else another option
we need to go and put in our set cookie
response so let's look back at the back
end code here
and
then where did we set the cookies well
let's look at the controllers that's
where the cookies were being set
and i believe the auth controller is
what sets the cookie specifically and so
not only do we have http only true
but then we went ahead and set same site
to none as they request here
and then after you do that
they give you another message in the
same spot and that says secure must be
set to true
now previously i didn't have secure set
to true there
because i thought you could only use it
with https even during development but
no it's working in the dev server even
though we're just using http
then after that i went ahead i've got
the code wrapping by the way because
down here on the next line you still
have the max age option
now when you delete a cookie
you need to set the same options however
and i'll put a link to the documentation
in the description below however max age
actually does not need to be set
or in there when you delete the cookie
it's one of only a couple like the
expiration and max age are the only two
options that don't need to be identical
when you delete the cookie so when we go
to the log out controller
and we have our res.clear cookie we need
to have http only same site and secure
all set as before but max age does not
need to be in here and we've got that in
here a couple of times
in the log out controller as well so we
did make those changes but after you do
that
everything should work as intended so
it's just a couple of things i
discovered as i was working with this
front end code and of course it is good
to test it out with actual code and a
browser besides using an extension like
thunder client because thunder client
showed everything was working as we
expected it to but of course we ran into
a few issues with chrome and now
i've walked you through how to fix those
as well
since creating the last tutorial i
realized there's a lot of confusion
concerning the concepts of
authentication and authorization they're
often used interchangeably or simply
abbreviated as off but they are not the
same things
authentication refers to the process of
verifying who someone is
authorization is the process of
verifying what specific resources a user
has access to when we log in with the
username and password we are verifying
who we are and that is considered to be
authentication
after logging in our express api issues
users jwt tokens jwt stands for json web
tokens while it's true that the tokens
confirm the authentication process has
already taken place these tokens also
allow access to our api endpoints which
provide our api data
this is authorization a hint towards
this fact is that a jwt token uses the
authorization header today we will
expand the authorization process by
adding user roles with specific
permissions to our api authorization
process we're going to start with the
code repository from the last tutorial
but if you don't have it you should be
able to easily follow along without it
or you can download or clone the starter
source code from the link i've provided
in the description below we're going to
start by going to the config folder in
the file tree and creating a new file
and we'll call this file roles
underscore list dot js
in the roles list we're going to create
the user roles now this could be in a
data table in a database this is just
how we're going to do this and you'll
find that user permissions can be
constructed in an assortment of ways and
we're going to apply a fairly simple
structure today with three different
user roles so we'll start out with our
roles list constant
and we're going to set this equal to an
object
and the keys will actually be the names
of the roles so we'll have admin and
then we'll have a code that identifies
the role
and then after that let's create an
editor role and we'll call this 1984 and
then let's have just a user role and
we'll give that 2001. okay now that
we've created our roles list object all
we need to do here is
module.exports
and set that equal to roles underscore
list
okay we've saved that now we're ready to
go ahead and modify
our users model that we have in the
users.json
file
and you might find that
once it has been written
by the read and write file process that
we use in node we're using this user
jsons file it might all be in one line
but i believe if we go ahead and just
put a return and edit and then save in
visual studio code it should go ahead
and format that file for you so it's a
little easier to read now this is what
we had after the last tutorial we have a
username walt1 and a username walt2 they
all have their own passwords and of
course we're tracking the refresh tokens
i'm going to copy in the file i've
created it adds one more user and so you
can copy this file
out of the completed source code that
i'm going to link to there's a starter
source code link and then a completed
source code link or you can go ahead and
use the register route create your own
new user and you can see i've added dave
one as well as our walt one and walt ii
dave one just has the role of user while
walt 2 has the roles of user and editor
and walt 1
is not only a user but also an editor
and admin so you can see we're going to
support multiple roles i'll go ahead and
save this file once again you can copy
it from the completed source code or
just create your own third user if you
want to
and we'll just edit these manually now
there could be
a admin area
for whatever service you were creating
and that is where these additional roles
could be created but what we will do is
just ensure when everyone registers
they're given the user role and then of
course later on you'd think an admin
that was in charge could add the
additional roles if those roles were to
be granted to other users
so now we can collapse the model folder
and the config folder but let's look
inside the controller folder and let's
look at that register controller that we
have what we want to do
is add that role of user with the 2001
code
when any user registers you can see
we're creating our new user right here
on line 19 and we have the username and
the hashed password so let's just go
ahead and add this extra roll i'll break
this out on separate lines
and that way we can see
the new part that we add and what we
want to do is just in between username
and password
add roles let's keep that lowercase
and after that let's put this in an
object so then we have
user
and then we have the code 2001 and then
remember to put a comma after roles
so it goes before the password
and save now let's go to the auth
controller
and when we authorize and create the
access token
we will want to send this information in
the access token so we're going to
change our payload for the jwts
now first when we know we have a match
and we have verified our user so
everything is good we want to go ahead
and grab the roles that we put in our
users json file so let's go ahead and
define roles
and set this equal to object
dot values and then we can pass in the
found user that we have above
and we want found user dot
roles and so now we'll get those values
inside of roles and now let's change our
access token payload just a little bit
since we're not just going to send the
username let's create a new namespace
here with user
info and then this will be an object
and inside this object we'll have
username
let me go ahead and add the closing
curly brace for the object here so we'll
have the username
but then oh we don't need that closing
curly brace but then we also want to add
some more information here inside of the
user info object
and the next one will be
roles
with quote and we'll just pass
the rolls that we've created it looks
like i actually did need that extra
curly brace or at least we need to
indent here
and then we're going to need to close
out our payload that looks correct i
believe and if you want to break this
onto another line you can do that as
well
go ahead and save and there we go it
gets formatted a little bit better
already so we're using this user info as
a different name space and that's good
because this is considered to be a
private jwt claim because there are some
res
reserved abbreviations and words for
public jwt claims and you can find out
more about that at
jwt dot io i believe i'll confirm that
and leave a link in the description
below so you can check out more
information on the jwt claims if you're
interested now there is no reason to
send the roles in the refresh token and
ideally the access token will only be
stored in memory on the front end but we
don't have control over that so when we
do send the roles we're just sending the
codes and not actually the word admin or
editor or anything like that
so we just are kind of hiding what each
one is by using codes but at the same
time ideally that access token would
only be stored in memory anyway but
there is no need whatsoever to send the
roles in the refresh token refresh token
is only there to verify that you can get
a new access token
okay now speaking of the refresh token
we need to go to the refresh token
controller and add some of the same code
because that refresh token does issue
a new access token so
right underneath where we have verified
the or we've actually decoded the
refresh token here and we have the
decoded and now we have no error and
everything is good so we want to come
down to this line right before we create
the token and here once again let's
define roles and set equal to object
dot values and we'll pass in our found
user once again and the roles that are
associated with that user and now we can
use the name new namespace as well so
we'll use user info again
and then we'll create an object
and inside this object
we'll have the username which is the
decoded username
but then we'll also
want to put the roles once again so
now we'll have roles
and here we can just pass in roles once
again i need a comma it looks like and
now let's save and it will format a
little bit better and that looks correct
so we have user info with username
and roles now that we've updated our
tokens to include the roles our access
token specifically we're ready to go to
middleware because we're going to have
to create some new middleware to verify
those roles but first let's look at the
verified jwt file that we already have
i'd like to make a couple of quick
updates one is when we define the auth
header this works and especially when we
have control of the front end as well
and we know we would define
authorization with a lower case a
however it can also come in with an
upper case so instead of this let's go
ahead and just say
dot authorization which is essentially
the same thing that we had but then we
can also say or and then we could have
request dot headers
dot authorization with a capital a as
well just in case it comes in with a
capital now we know we're going to grab
it either way
after that let's go ahead and change our
if auth header as well
and let's look for
an optional chaining method here so we
say if we have or if we do not have the
auth header actually are saying
and also
starts
with
and here we want to have
bearer and
this is supposed to be a capital b by
the standard so we don't really have to
look for lowercase and uppercase like we
were here so we're verifying
that
first of all if we do have an off header
or if it starts with this and we're
actually looking to say if we don't have
an auth header and then this optional
chaining says okay well even if we do
have an auth header if it doesn't start
with bearer with a capital b and then
the space after bear then we're going to
return a 401 because it is not a
correctly formed
authorization header that starts with
bearer and then the token as it's
supposed to it looks like we left in a
console log from the last tutorial let's
go ahead and take that out where we were
just viewing the bearer token in the
console
and now after we decode
this token when we're verifying the jwt
we also want to set the roles here not
just the user on the request so let's
have request
dot roles and we'll set this equal to
decoded
and now we've got a different namespace
so it's dot
user info
dot roles and we need to make the same
change up here when we get the username
because now it's decoded dot user info
dot username
and now we've got the correct namespace
for both and we can save our verify jwt
middleware and now it's time to create
our new middleware i'm going to collapse
the open editors so we have a little
more room and in the same middleware
folder let's create a new file and let's
call this
verify roles.js
inside of verify roles we're going to
create middleware named verify roles
and it's going to accept
a lot of parameters if we want it to
it's however many roles we want to pass
in and the way we do that is with the
rest operator it looks just like the
spread operator but it lets us pass in
as many parameters as we wish and we're
just going to call them allowed
roles
now from there we need to go ahead and
have a middleware function and you know
that takes
a request response and next so what we
need to do and this allows us to pass in
the allowed roles by having this on the
outside but we need to return
a middleware function essentially an
anonymous function here with request
response
and next
and now inside of this function we'll
have everything that our middleware
would do so the first thing we need to
do is say if
we do not have a request which we should
because our verified jwt will come
before this
but let's go ahead and do that just to
be thorough and then let's use optional
chaining to say okay even if we do have
a request it needs to have roles
or
this should not be valid and if it's not
valid
we're just going to return a response
and send
the status
401 which is unauthorized now we need to
define a roles array let's keep that
lowercase camelcase actually rolls array
and set this equal to
the allowed roles that were passed in
now we're spreading those into a new
array here
now that we have this let's go ahead and
log this to the console these will be
the different roles that we're passing
in of course there will be the codes
associated with the roles but let's go
ahead and log roles array so when we go
ahead and test this we can see
everything that we expect to and we'll
be comparing this to the request dot
roles that we just set
inside of the verified jwt that will be
executed as middleware before the
verifies roles middleware
okay so we'll log both of those just so
we can see what's going on but now we
know
we have an array
of user roles that are coming from the
jwt
and then we have the roles that are
passed in that will be allowed and
that's what we're going to compare so
we're comparing arrays and i'm going to
define just a result here
and let's set this equal to request
dot
dot map which map creates a new array
and we'll have a role
and for each role essentially we'll
compare to the roles
array
and we'll see if the roles array
includes
the role that we're passing in and if it
does it will return true
that's what includes does and if not it
will return false it's a boolean so
we'll have a new array and we'll have a
true or false for every
thing that was in
the request role so
if we had three different roles here
we'll have possibly true true true or
true false true
who knows for sure but that's what we'll
have so we need to filter this array
and all we need is one true to know that
the role
can access
the route that we're
verifying so what we want to do then is
chain
find and we can just say for each value
the true and false in the new array that
was mapped
each value will check the value to see
if it's equal to true
and if it is
it will return if it finds a match it
will return the very first one it finds
or if it finds no matches then we won't
have a result and so that's how this
works i'm using two higher order
functions here essentially so we're
mapping over
the roles that are sent
from the jwt and they're assigned in the
verified jwt to request roles and we're
mapping those comparing them getting
true and false
results back to the roles array that
will be passed into this route so we'll
pass in
however many roles we want to this array
could possibly only have one role or it
might have three or four or however many
we're checking for or want to allow to
this route
so once we compare those and get all the
true false results we're just using find
to say hey find the first true
and if there is any truths it will be
good and if there's not it won't and
that's what we'll do next so we'll just
say if
there's no result
essentially we did not find a true
result we're going to return
and then we'll say res
send status
and again we'll send a 401 unauthorized
otherwise we'll just call next because
everything's good and we're ready to
move on and we're going to let the route
be accessed so let's save our middleware
and we'll know when it runs we'll see
the arrays that we're comparing
in the console and before i forget we
also need to add module dot exports
and set that equal to verify
roles and save
now let's collapse the middleware folder
and open up the routes folder and then
the api folder and let's go to our
employees.js that has the different
routes get post put and delete we have a
couple of imports to make here so
let's go ahead and define our
roles list
and set this equal to require
and now we need to go up
and up again and then we'll look in the
config folder and then we'll find our
roles list
and after that
we need to go ahead and define our
verify roles middleware
set this equal to
up a folder up another folder middleware
and now we're going to verify roles
and i'll save just because i've
completed the imports but now we need to
add this to the different routes now
let's just leave the get route open
because
any
one could access that or if we wanted to
put verify roles and at least verify
their user but they already have to have
the jwt because we required that ahead
of time so
they'll have to have the jwt to access
the git route so kind of the user is the
default but after that
in the post route let's put in
verify
roles
and now we can pass in
the different
roles list
values that we want to let access
this route so we'll type
roles underscore list and now dot
admin will work
remember
this is the key and then the code is the
value so admin is the key
and then we could also put in
roles underscore list dot
editor
and that makes sense because you could
have an editor that could post a new
value as well okay then i'm going to
copy the verify roles that we just added
here
and
it's like maybe i'll put a space here
and then input i'll do the same thing
and add the space but now in delete
let's say only
an admin can delete anything from our
database so we'll save that which gives
it just a little bit of a change so now
everyone can access the get route
but the post route should be limited to
any user that's an admin or an editor
the same for the put route but then the
delete route could only be used by an
admin i think we're ready to test this
out now so let's go ahead and open a new
terminal window you can do that from the
terminal menu or i'll just use control
and backtick i'm on windows and i'll
type npn
run
dev to get our dev server up and running
with our api
and then we'll test this out with
thunder client okay i'm going to drag
the terminal
window up just a little give it some
more room so we can see the console log
notices that we get and from there i'm
going to click on thunder client here on
the left if you don't have thunder
client installed you can get it through
the extension over here and then just
search for
under client
and there you see it
and now that you have it or once you
have it you'll be able to click on it
over here on the left circle with the
lightning bolt
and you can create collections and of
course we've done this in some previous
tutorials if you have followed along in
the series i've got an auth collection
here and the very first thing we'll need
to do is authenticate a user let's look
at which user we're going to
authenticate first let's just do dave
one remember
this user only has the user permissions
it does not have an editor
or an admin permission so i'll send
and i get the access token
i'll copy this access token
and now when i go to
the employees api and go to get
employees
i'll go to auth here
paste in the new access token and send
and everything is good but if i do this
for post if i'm in time within the 30
seconds it was given
and send i got a 401 unauthorized so
that means our verify rolls is working
as we look down here
we've got the two different arrays
logged to the console too so
this
is the array that has the roles that we
were looking for that we passed in 5150
is the admin 1984 is the editor but you
can see our user only had role 2001
which is just a user okay now let's go
back to my auth collection here and the
auth route and let's change
who we're getting a token for so now
this will be
walt 2 he is an editor
and
also a user but he is not an admin so we
can send this we get a new token i'll
copy the token
and now we can go back to the employees
api and we can post a new employee or we
should be able to we'll put in the new
token here
and send and yes we posted john doe so
he's now number three has the id of
three in the employees list but if we go
to the delete route
and go to auth i'll post in the same
token and attempt to delete
and oh we're out of time we got
forbidden so let me go ahead and try to
re-verify here
we once again got walt 2
we get a new token
and get that new token once again i
didn't copy it right the first time
there we go back to employees now we'll
attempt to delete i'll pass in the auth
token
and send and we got a 401 unauthorized
and that is because the role did not
verify now you can see you had to have
the 5150 code which is admin that was
passed into the route
but
walt 2 only had the 2001 and the 1984
code in his roles list okay once again
let's go back to the auth route now and
we will change to where we are
authorizing
walt one walt one should be an admin as
well as a user and an editor
and so we'll send that get a new token
and now we can go back to our employees
delete route that was just denied to
walt to
paste in this new token
and attempt to send the delete request
and employee id 3 is not found that's
because nodemon restarted i believe and
of course employee id 3 isn't in there
so let me go ahead and request another
token and we'll see if we can create id3
again quickly and then delete it on the
same so here's our new token for our
admin user
copy that go to the employees post route
paste in the auth and created now let's
go to delete
paste in the new auth and we deleted
with an okay so i just had to be quick
enough to beat the 30 seconds with that
token but here you can see the user
our walt one user had
2001 1984 and 5150 in his roles and
the api route needed the 5150 so when it
matched everything was good hey just a
quick note on testing with thunder
client and i want to point this out in
the controllers
and then under the auth controller but
when you're testing with thunder client
it honors the cookie setting for secure
true or not so if we were to set a
cookie and then use the refresh token
here you would need to remove
this or the cookie would not work with
thunder client however this is required
as i noted in a previous tutorial when
working with chrome so
just a note if you're testing the
refresh endpoint with a refresh cookie
you'll have to comment this part out or
at least take it out for testing
purposes with thunder client but then
when you work with chrome and in
production you both want that secure
true back in here when you create that
refresh token that is saved in a cookie
we did not use that today and the
refresh token does not store any
information about the user roles and it
shouldn't however just wanted to note
that for you in case you were testing
out that refresh token with thunder
client so there you have it we created
our verify roles middleware and you can
see
both of those that we've been logging to
the console i'll go ahead and delete
those now before i commit it to github
and get the console logs out of there
there's our verify roles middleware and
you of course saw how we applied it to
the routes in our api
for our employees and you can just pass
in
the different roles that you want to let
access
that given route
mongodb is the m in the mern stack it
represents the database in the stack the
front end application of the mirn stack
is handled by react along with nodejs
and express mongodb completes the
backend rest api
traditional sql databases are built in a
relational structure
related tables reference each other with
joins as data is queried these
relational tables also normalize the
data that means data is not duplicated
in the tables that's the dry principle
which stands for don't repeat yourself
and that's applied to the structure
however with no sql databases like
mongodb you can throw all of that out
mongodb stores data in collections the
individual records in the collections
are called documents the documents have
a key value structure and look a lot
like json a collection holds all of the
data about a user for example instead of
breaking it into related tables and
likewise duplicating and distributing
the data where deemed necessary in a
nosql structure is permitted
so why choose nosql databases and what
are the advantages of using mongodb
performance is key
the speed at which a collection is
queried is very fast
flexibility it's very easy to make
structural changes like adding a new
field without wreaking havoc in your
total structure it is much like adding a
new property to an object
scalability
nosql can support large databases with
high request rates at a very low latency
and finally usability as you'll see
today we can get up and running with
mongodb in the cloud very fast
let's get started by going to
mongodb.com
okay we're at
mongodb.com and i already have an
account so i'm going to choose sign in
if you don't have an account you'll want
to sign up for a free account so you can
just click try free i'm going to click
sign in
and then it will take me to a page that
probably uses my google id or allows me
to log in with an email address yes
there it is so you get those options i'm
going to log in with my google email
address or my google account
and then we'll meet back up after you
have your account or you've signed into
your account
okay i'm signed into my account and i'm
on the projects page where i can create
a new project and you see i already have
one project here now if you're not on
this page once you're signed in just
click the little leaf in the top left
and it says view the organization home
because that's where this is so once
you're there you'll want to create a new
project
and now you want to name the project i'm
just going to name this one
tuts short for tutorial and click next
and then it asks you to go ahead and set
permissions or members and it'll
probably assign that to your default
account to start out with as you can see
i have project owner right here so i'm
going to click create project with the
project created you can see i now have
tuts up here above database
deployments we need to build a database
and of course it gives you the big shiny
button right in the middle to do so so
please click that
and then it gives you choices and we're
just going to go with free today if you
want to get one that you pay for that's
fine i'm going to choose free over here
on the right
and then it will say create a shared
cluster
and right now i'm just going to keep all
the defaults so free shared it has aws
it has one of the usa
regions for me because i'm in the u.s
you may want to pick a different one if
you're not and maybe it already defaults
to something close to you
and then it just has these other default
settings and i'm going to go with all of
that even the default name here cluster
0 and click create cluster at the bottom
now it says new clusters take between 1
to 3 minutes to provision so i'll come
back when this is finished my cluster 0
has now been created and we're given
this screen so what we want to do is
click browse collections
we don't have any collections yet so we
get this and it says load a sample data
set or add my own data we're going to
choose add my own data
with that it asks for a database name
and a collection name so let's just call
this
company db
and then we can call the collection name
let's go with
employees and i'll just keep that all
lowercase companydb
is capital company and capitaldb at the
end and i'll click create
and with that has created our
companydb database and our empty
employees collection right here what we
should do now is concern ourselves with
database access so let's create a user
that can access
this new collection and database that we
have so create a database user we click
the big button that says add new
database user
and then it gives us password
certificate and all of that we'll just
stick with password
and it put in some old information for
me let's put in something different here
let me go with
tut once again
and then for a password
i'm just going to and we'll go ahead and
show whatever the password is i'll go
with
testing one two three kind of like a mic
check
okay so we've got tut testing two
testing123 i think we'll keep all of the
default options here and we want the
read and write to any database and of
course i'll come back later and
delete this user but for now
we'll use it for the tutorial and we'll
click add user
and once we have the user we need to go
back to our cluster so
let's click tuts
and that takes us back to our cluster
and it says we are deploying your
changes current action configuring
mongodb
so i'll give this just a second and then
we're going to click connect with the
configuration now complete let's go
ahead and click the connect button
it tells us we need to set up some
security this part is required and what
we're going to do is allow access from
anywhere we don't really know where
we're going to host our back end yet so
this is good for development right now
until we actually know what i p address
we had so let's just add in the zeros
and that means it's good from anywhere
we already created a user so now let's
click choose a connection method and
we're going to connect our application
this gives us a connection string and
this is what we want notice it's already
put in the tut user i created now
i'm going to have to put in the password
including getting rid of the
less than and greater than
around the password and then also i need
to replace my first database with the
companydb which was the name we gave the
database so i'm going to copy this
and we're ready to close out of this now
and open up visual studio code we're
going to start with the code repository
from the last tutorial in the nodejs and
express and now mongodb series but if
you don't have it you should be able to
easily follow along without it or you
can download or clone the starter source
code from the link that i'm providing in
the description below so let's get
started by going to our dot env file
and inside the dot env file it looks
like i need to go to the end of the line
and hit return so we have another line
i'm going to create
a database
underscore uri
and set it equal to our connection
string but now we just need to replace a
few things in the connection string and
i'm going to press alt z here i'm using
windows and it will wrap
the code in visual studio code it may be
different for you on mac or linux if you
have those okay so we're replacing the
password and if you remember i put in
testing lowercase
one two three
and then instead of my first database we
named the database
company
db
now we can save this file because we're
using the process.env
to pull this value out when we need it
to connect and speaking of dot e and v
we actually put it in about three
different files before and really we
could have just put it at the beginning
of the project it's not like when we
require other
things like the jwt here that need to go
in individual files as they're needed
this can just go at the beginning of the
server.js so i'm going to pull it out of
our middleware verify
dot jwt.js file and save that and i
believe it's in a couple of controllers
that we can remove it from as well but
in the server.js file i'm going to put
it at the very top you just want it in
there as soon as you can get it
and we'll save it and now i believe
let's check these controllers real quick
is it in the auth controller
yes it is we can remove that
and it's in one other controller if i
remember right
not
the logout controller
see if it's the refresh controller there
it is and we can remove it from that as
well and i think that will take care of
it so let's close out of the controllers
it didn't save the auth controller need
to do that too
okay close out of those and we're going
to do some work here in the server well
really before we do anything in the
server we need to go to package json
because we're going to install mongoose
and i'm going to press control and the
back tick you can also go to the
terminal window or menu to open a new
terminal window and here we need to
install the mongoose package so it's npm
i
mongoose and return now mongoose.js is a
library that makes working with
much easier kind of like you could
consider react makes working with
javascript easier than vanilla
javascript it's just a helpful library
so let's take a look at their page real
quick it's
mon goose there we go js.com
we bring up that page and you can see it
says an elegant mongodb object modeling
for node.js perfect exactly what we need
and it has some great documentation as
well so we can refer to this as we go
today we'll be working with a connection
let's go back to vs code and see if our
package installed it looks like it has
so let's check our dependencies and we
see mongoose listed right here on line
20 so we are ready to begin working with
mongoose so now let's go to the
server.js file and underneath
credentials let's go ahead and require
mongoose so we define const
mongoose and set it equal to
require and then we just bring in
mongoose
and save
now after that we need to go ahead and
create a connection configuration so as
you might guess we're going to do that
in our config folder that we have up
here so i'll highlight that and click
create new file and i'm going to call
this lowercase db and then uppercase
con so it's camelcase really db con
dot js now let's start this file by
requiring mongoose as well so we'll once
again bring in mongoose
and require
mongoose
and now we need to create a function
that we're going to export and let's
call it connect
db and set this equal to an async
function mongoose is async let's go try
and after try we'll have our catch
with an error block here
and we'll just
console.error and pass in the air
and then up above we need to go ahead
and try to connect a mongoose so how
we'll do that is a weight we've already
got a sink above and then we use
mongoose and we choose connect
and from there we need to bring in
that db uri that we defined back in our
dot env so we can just refer to process
dot
env dot
database underscore uri
and then there is an object
and what we need to do is pass in a
couple of options that will just prevent
warnings that we would get from mongodb
otherwise one is
use
unified if i spelled that right
topology and we set that to true
and the other one is use new
url parser and we'll set that to true as
well
and then we should be pretty much
finished except we once again need to do
module.exports as we have many times
before four functions we've created and
exported
and it is connect db and we can save the
file with our function created let's go
back to server.js now and we need to
import that so
say const connect db is going to equal
require
and then we need to go into config
and from there we need to pull in db con
and save and now let's go ahead and
connect to the db the very first thing
if it fails we don't want to listen for
any other connections anyway so this is
connect to mongodb and all we do is call
connect
db right here
and it's ready to connect however there
is one issue to address here so let's
scroll all the way down to where our app
listens for requests now we don't want
to listen for requests if we don't
connect and so
if our connection fails for whatever
reason we need to avoid doing this
and we can do that let's go back to the
documentation for mongoose and where we
had connections here in the
documentation we also have connection
events
and let's look we can listen for the
connected event
and there's also the open event which is
equivalent to connected so let's go back
to our code and use that
and we can do that with mongoose which
is why we required it for this file in
particular
and we'll have mongoose
dot
connection
and then we want wants instead of on
because we'll just listen for this event
one time
and we'll listen for the open event it's
a little shorter to type than connected
and now we'll have
an anonymous function and inside this we
can console log first
and inside there we'll just say
connected
to
mongodb
and after we've connected to mongodb
let's just go ahead and cut and paste in
our app listen
and we'll put it right there
and now we're only going to listen for
requests
if we have successfully connected
because that is when the open event will
be emitted by the connection and so then
our console will let us know we've
connected to mongodb
and we're listening for requests on
whatever port and of course here in
development we are defaulting to port
3500 as set at the top okay all of that
said i've left the terminal window open
let's go ahead and type npm
run dev
and i'll drag it up so we have a little
more room and let's see if we connect
and we're connected to mongodb
and the server is running on port 3500.
okay in the next tutorial we'll create
schemas and data models with mongoose
and relate that back to our mongodb
collections
in the previous tutorial we went over
what mongodb is and we used the mongoose
js library to connect our app to mongodb
today we're going to create mongoose
schemas and data models that will allow
us to perform crud operations on our
mongodb data collections we're going to
start with the code repository from the
last tutorial
but if you don't have it you can
download or clone the starter source
code from the link i've provided in the
description below let's get started
today at
mongoosejs.com and from there we're
going to click read the docs and it
instantly takes us to the schemas in the
docs and that is because as you see on
the page all highlighted it says
everything in mongoose starts with a
schema
each schema maps to a mongodb collection
and defines the shape of the documents
within that collection so you can see
why they're very important and here you
see an example schema they have a blog
schema and they're declaring data types
for the different fields inside of the
documents that will be created with the
data model
and they also have some options here
such as date they're not just saying
it's a string for the body they're also
saying it's going to be a date they can
have default data and there's other
options available as well and if we
scroll down just a little bit further
you can see the permitted schema types
and here we have string number date
buffer boolean and a few others object
id is an important one that will be
created automatically for us so we will
not have to specify an id as you don't
see one specified in their example
either so now let's go to visual studio
code and get started creating our
schemas for both our employees data and
our users data okay i'm in visual studio
code and i've got the source code from
the last tutorial it's linked to below
as the starter source code if you want
to download or clone that right now
we're going to go to the model folder
and we've got
employees.json and users.json because we
were just using the nodejs file system
to write to both of these files and
we're going to get rid of that
eventually here as we replace everything
with mongodb for now just keep those
files and we'll create
new files for our schemas and the first
one will be called employee
and i'll spell that with a capital e and
then just js and that's a naming
standard it won't really have an impact
if you don't use the capital e however
it's pretty much the consistent standard
that i've seen so first let's define
mongoose
and require that
there we go
and now that we've got mongoose pulled
in we also need
a schema with a capital s and that's
going to equal
mongoose dot
schema once again with a capital s
now that we have that we can define our
schema and let's call this
employee
schema and that can use camel case
we'll set this equal to a new
schema once again capital s on schema
and now we can map out our data and if
you remember our employee's data was
very simple it just had a first name and
a last name so that's what we'll declare
here now remember
uh we will automatically have an object
id created for us so we don't need an id
field here
and now we can say first name is type
string and then we can also say it's
required and that accepts a boolean so
we'll say true that is absolutely
required and then we'll do the same for
last name and know what we could just
copy this down
shift alt and the down arrow for me on
windows and visual studio code
and make that the last name
and let's get rid of the comma there and
that's basically our schema very simple
we've got two string fields first name
and last name now at the end of the file
we need module dot exports
and we're going to set this equal to
mongoose
dot model
now we're creating a data model right
here and we'll set this equal to
employee
and that is uppercase
first letter and then not plural once
again just like the name of the file
employee
and then we'll use the employee schema
now by default
mongoose when it creates this model
will set this to lowercase and plural so
it will look for an employee's
collection in mongodb
and the employees collection will be all
lower case and once again it will be
plural and we can see that if we go back
to the docs and we go to the models link
in the docs there we go
it says this in bold right here mongoose
automatically looks for the plural
lowercase version
of your model name so in the example
they give they have the word tank they
defined here for the model and they
passed in
the string tank with the capital t
but it says for the example above the
model tank is for the tanks collection
in the mongodb database and that's all
lowercase and plural all right back to
visual studio code let's save this file
and let's go ahead and create a user
schema as well now our user data had
just a little more to it so this will be
a little more interesting than just the
first name and last name that we had in
the employee schema i'm just going to
copy these imports here because we need
the exact same thing at the beginning of
basically every schema we create
and now i'm going to say user
schema camel case
this is equal to new schema capital s on
schema
and now we have a username in our data
and this is kind of like the first name
last name in the previous schema it's
type
string
and then required
is true
after the username
we have roles
now the roles data is a little bit
different so we had
user which was a possible role
and now let's specify
some data or some details about the user
role here inside of an object and now we
can set this to type
number
and we can put a default value so if not
specified
any user that's created will
automatically be assigned the value of
2001 and that was our basic user value
that we had previously
applied
and then the next role was editor
and that had a number
and then the next role was admin and
that had a number notice we're not
providing default values and we're not
even saying they're required not
everybody's an editor not everybody's an
admin so these will only be added to the
data that we decide to add them to as an
admin could make those decisions okay
now that we've defined the roles let's
put in the password field
this is much like we had before type
will be string
and required
will be true
and then after that if you remember
we also store a refresh token
and when a user is created they don't
have a refresh token but after they're
authenticated they get one so this is a
string
but it does not have a default value and
it's not required because it's not
always there
so now we've created our schema
for our users
and let's go ahead and export this
we'll say
module.exports
set it equal to
mongoose.model so we're creating a data
model
and this will be user singular and then
mongoose will once again look for users
all lowercase plural
but we'll match our file name so user
and then we'll use the user schema that
we just created
okay we've created two basic schemas for
our data user and employee and then we
created models to be associated with
those let's go ahead and implement the
user schema and model
and let's attach that to one of our
controllers
and let's do it with the register
controller first that makes sense where
we'd create a new user and now you'll
see how much easier mongoose makes
interacting with the mongodb collection
than it is to write all of this stuff
that we have been doing with the file
system module and interacting with json
files so let's simplify this file and
just switch it over to using mongodb so
we're going to get rid of the user db
that we had up here
and let's just bring in
the user model so i'll define user with
a capital u and set this equal to
require
and now we need to go up out of the
controller folder and into the model
folder
and then we need
the user model
after we do that whoa i lost something
there
user model
there we go after we do that we can
really get rid of the fs promises and
path we're not going to be right into a
file anymore or need that we do still
need to keep the bcrypt import and now
let's start making changes as we handle
the new user here
and it'll start out the same we need the
user and password to come in from the
request and if we don't have those we're
going to send the same information back
that's a bad request
but now when we check for duplicates
we're interacting with a different
database so now this will change just a
little
and we'll still define duplicate but how
we get the duplicate is completely
different
notice we already have handled a user as
an async function so right here we're
going to await
and then use our user model
and call
find one and now we'll pass in some
information here we're looking for a
username that matches
the user that we defined from our
request
and after that we need to call
exec here at the end now not every
mongoose method needs that on the data
model but this one in particular does
and that is because we could pass in a
callback afterwards like a error result
for example but if you don't do that and
you're using the async a weight
pattern here
then you need to put exec at the end of
find one and you can check that in the
documentation under find one to
reference
if you need that or when you use any
method that you're not sure of and
you're using async await you should
check that but this is going to return
any user that matches the user that was
passed in and of course we don't want a
duplicate so this is the same if there
is a duplicate we need to send this 409
conflict there
after that
we need to handle the password in the
same way but after the password is
created our code is going to get much
simpler let's go ahead and keep the new
user even though we will define this a
little bit differently
but we're not going to use this or the
fs promises we don't need that console
log there let's go ahead and keep the
status 201 though because that is what
we want to send when we create the new
user with mongoose we can create and
store so i'll put create and store the
new user all at once and so what we'll
do
instead of defining a new user here i'm
going to define
result
and i'm going to set result equal to
a weight
then our user model
dot
create
and now we are creating a new user and
let's look inside to see what we're
passing into the create to make sure we
have everything we want or we don't want
what we do want the username
and then we don't really need the roles
here because remember we have the
default data in our schema so it will be
added automatically so we can remove
that and then we pass the password and
an object id will be created
automatically also so this is all we
really need to send
through our data model
and the username and password
will be sent by us and then a role will
automatically be created a roll value
and an id
for the document will also be created
now result will return the record that
is created so we could go ahead and log
the result just to view the record in
the console and i'll save that i do want
to discuss a couple of other ways a
record could be created that you might
see somewhere i prefer to do it this way
because it happens all at once just the
user create and it is created and we get
the result back but you might also see
something like
const
new user equals and then a new user with
the model and then of course you could
set
new user dot i'm sorry user name there
we go and set that equal to whatever
data you had and use uh dot notation
that way to set the values and at the
end of all of that you would want to
save so then you would say const result
equals and then you would have your
await
new user
and you would call save and that would
also work however that's just a little
bit longer process
you might also see something like the
new user being created and then passing
that data in like we did inside of user
create so instead of using dot notation
you might see something like
new user
equals
well here we define new user but then
it's equal to a new user and you're
passing in the values inside of the
parentheses there and then you would
still need to have the result equals
await new user.save after you did this
and so this happens
all at once when i use user create and
if i do it this other way and create a
new user then you have to have another
line to actually save the new user so i
prefer the user.create
let's go ahead and save this
now we're ready to test our register
controller route
so let's open up a terminal you can do
that from the terminal menu or press
control back tick at least i can on
windows and now i'm going to type npm
run
dev
and that should start up nodemon and
start our api server here just on
localhost port 3500. we've got our
message connected to mongodb
server's running on port 3500. i'm going
to use thunderclient to test everything
out like i have in some previous
tutorials if you don't have it you can
install thunder client as an extension
and then it's right here after you have
on the left
i'll click that i've got some
collections and under auth i should have
a registration route to test and now i'm
going to hide the left-hand
menu over here because it looks much
better and it's easier to read when it's
the full screen so here's the register
route
see what we're sending in the body okay
i've got a user named steve1 to create
and then it's got a password and that's
really all we need to send let's see if
the register route works
new user steve one created and here's
our object in the console so now we can
see what we've got
username steve1 the role was created
with user 2001 we've got our encrypted
password and we've got a new object id
and then notice this other field as well
that's always added the two underscores
and the v and the zero you should always
see that as well if you're curious the v
is the version key and it keeps track of
this and we can also increment this
manually if we want to okay now that we
created steve one let's create another
user and i'm going to go back to my wall
one that was an admin previously so
let's create him as well
and now new user walt one was created
and if we look here in the console we
can also see
that
walt one was created down here and
here's his information now let's go to
mongodb
and inside of mongodb
we can check
our company db here and we should be
able to refresh
and now we have a user's collection as
well and you can see it has two
documents so let's look at the user's
collection all right we load the
documents up and here are the
two records or we could call them
documents really i'm used to saying
records with sql this is a nosql
database and it has collections instead
of tables and it has
documents instead of records so in
either one of these we can expand
the object and see our user 2001 that
was created so we want to make walt an
editor and an admin so inside of mongodb
here we're mongodb.com and logged back
into our account and we're looking at
our collection
inside of the companydb database that
was created and we'd created the
employees collection before
but we had not created users so when we
created the first user it also created
the users collection which is
interesting but after that
we want to edit walt here and we can do
that right inside of
mongodb.com so let's edit and now let's
go ahead and add
another role add field after user
and we'll put in editor and the value
for the editor
was 1984. okay now we've completed that
now let's add field after editor
and we'll add
admin
and the value for the admin was 51.50 if
i remember correctly
and so now we've gone ahead and changed
that information but we still need to
click update here
to
update the document now the document is
updated and walt
has all three roles in his document oh
but notice what happened here and you
want to catch this if you do it because
i certainly just made the mistake this
was entered as a string and this should
be numeric data so we want to get rid of
that we don't really need to remove that
we need to change the data type over
here on the right notice how
user 2001 is int 32 so we want to choose
that type here on the right
and make sure
we have the right data type as we make
changes here on mongodb.com to any given
document okay those changes have been
made now let's update and now we have
the correct data type in waltz roles
2001 1984 and 5150. okay going back to
vs code
and i'll close out of thunder client
and close out of the terminal as well
but let's go ahead and show the file
tree because
we have changes to make in the
controllers we need to go ahead and
implement our user model instead of the
json file we've been using in the
refresh token controller the logout
controller and the auth controller and
then we need to implement the employee
model in the employees controller and
change how some of this works just like
we did in the register controller and
that's what we'll be doing in the next
tutorial but my challenge to you is
before then go ahead and make the
changes on your own and then compare to
the tutorial that i release that makes
the same changes
today we're going to apply the user and
employee models to the remaining api
routes and update the asynchronous crud
operations accordingly and before we
finish i'll show you an easy way to
deploy your rest api to the web we're
going to start with the code from the
last tutorial if you don't have it i
have a link to the starter source code
in the description below let's get
started we're looking at the user schema
and then of course we create a model
here at the bottom that's where we're
starting and we applied it to the
register controller near the end of the
last tutorial
and you can see we imported in the user
model and this replaced what we had
previously in the tutorial series which
were
json files now we're using mongodb
and we're interacting with mongodb by
using the mongoose schemas and models
that we've created so now let's apply
our user model to the next route and
instead of register let's just move up
one and go to the refresh token
controller okay you saw i copied the
user model import from the register
controller file and i'm going to paste
that right over the user's db that we
had previously defined to interact with
the json file so now we're bringing in
the user model and now we can update the
handle refresh token function we'll
start out the same here where we define
the cookie
and of course get the jwt from the
cookie if it exists but then as far as
checking to find a user we have to do
that differently and let's go back to
that register controller for just a
second because we did a very similar
thing we used user dot find one and we
passed in the name of the user well this
time we won't have the username but
besides that it will be very similar so
let's highlight all of this where we
were finding the refresh token
inside of the json and let's paste in
our user find one but now instead of the
username we can just put refresh token
here and that's because
the property name and the value name or
the
variable name are the same so instead of
refresh token colon refresh token we can
just put refresh token there we still
need
the exec here to execute and that is
because we're using async a weight and
that means we need to put async up here
above which i almost forgot so let's put
async up here by handle refresh token
and make this an async function and
we'll use await here and then we search
for the refresh token
inside of a document within the user
collection after that everything
essentially remains the same because we
have defined the found user if we found
the refresh token or not and of course
if not it's a 403 forbidden but after
that we're using the same definitions as
before so there's nothing else to change
in the refresh token route so we can
save the file
and move on to the logout controller
okay we'll start the same in the logout
controller and we will not need the fs
promises or path either so we can just
highlight all of that and paste our
that's not the user model is it so let's
change that control z to undo that i'll
go back here to the register controller
where i copied
the user model import before i'll copy
that again
back to the log out controller
highlight all of that and paste because
that's what we need is the user model
imported after that we already have the
note here on client also delete the
access token again this is the log out
route and that's fine
the next three lines just as before
are fine and then we need to say is the
refresh token in the database well that
sounds familiar that's just like we used
in the refresh token controller so we
come back to the refresh token
controller i'll scroll up where we had
our await user find one and passed in
the refresh token
copy that come back to the logout this
is already an async function that's good
so now we can once again define the fine
found user
in the same way
and now after that this remains the same
but now this gets much simpler where we
delete the refresh token we're no longer
interacting with any of this json here
so let's just highlight all of this
and delete and we can make a few changes
we can say found user
dot refresh token
and we can erase it simply by setting it
to an empty string
and then we can set a result and we'll
set that equal to await
found user
dot save and that will save our changes
back to our mongodb document that is
stored in the user collection and after
we do that we could go ahead and log the
result if we'd like to see that because
that will return
the newly updated information
and that's just for our purposes there
you would of course delete that before
you put it in production and that's all
the changes so we can save that and
again
remember that found user this is a
document now that we have found if it
existed and therefore we're able to
update that document with the save okay
i'll scroll back up to the top and i'm
going to copy that user model import one
more time because we need it in the auth
controller
once again i'll highlight the user's db
that we previously had and paste in the
user model instead i'm going to go ahead
and delete this line between bcrypt and
jwt we need both of those but we can go
ahead and once again delete fs promises
and path which we should no longer need
these first two lines are the same we'll
keep both of them but then once again
with found user i think you see the
pattern now now we're back to looking at
a user name though so we can take this
and go back to the register controller
where we had the user name with find one
and copy that line go back to the auth
controller and then we can paste that in
as we find the user
and save because the next line is fine
as well and then
the password is evaluated and we're not
changing anything about that and really
we won't make any changes until we come
down here once again to where we were
interacting with the file
and we will be saving the refresh token
with the current user so once again we
can delete everything we had here down
to the response cookie
and let's leave a blank line in between
and then we can once again say found
user which is our document that we have
found
and set the refresh token
we're going to set that equal to
the refresh token that we now have here
in the auth controller
and then let's define result again
and let's set result equal to await
found user
dot save and that will update the
document back in mongodb and let's go
ahead and log that result again as well
so that should also look very familiar
and that completes the auth controller
so i know we tested the register
controller the register route in the
last tutorial but now we'll want to go
back and test all the rest of these so
we've got the refresh token route we've
got a logout route and of course we've
got the auth route as well so now let's
test those once again with thunderclient
if you don't have thunderclient
installed it's an extension for visual
studio code
and you get that right here under
extensions and search for thunder client
and then you'll have it on the left
and this is thunder client it allows you
to save collections which i've already
saved to match up to this but before we
can test it we actually need to start
our dev server i'm going to press
control back tick you could also go to
the terminal menu at the top and choose
a new terminal window that way then we
type npm run dev to launch our dev
server
and we should be running on 3500 and
connected to mongodb here in a second
when it gives us our information and
there we see both connected to mongodb
server running on port 3500 so it's now
ready to test so i've saved a collection
named auth and then here we have
different
routes if i can see which route we have
here looks like this is the register
route let's go ahead and try that i'm
going to click thunder client to hide
the left hand
side so we can see better and let's look
at the body to see what we're going to
register we're going to register a user
wall one with the password i believe
he's already registered so we should get
a 409 conflict back let's see what we
get
that's exactly right 409 conflicts so
let's change this
to tom one i don't believe i have tom
one in my collection of users so now
we'll send this
and 201 created so we have a new user
created our register route is working
now let's go back to thunder client and
look at the other routes that was the
register route now that we've registered
let's test the auth route now let's see
who we want to authorize well i believe
walt one was already created we
confirmed that so we can just send in
this information to authorize and we
send it in and we've got a new access
token back
and everything is okay he is authorized
and of course you see down here
we're logging these responses that we're
also getting back these console log
statements that had the result
and that's what we see here is the user
that we've pulled in is walt one okay
i'm going to pull this down and i'm
going to authorize again because if you
remember right
we only get an access token the way we
have it set that works for about 30
seconds but really i guess we're not
testing that so wait a minute when we're
not too worried about that because we're
going to test either the log out or
refresh token route let's test that
refresh token route that i have in a
separate collection
and so with the refresh token we're
sending a cookie it says no cookies
available but i think we have a cookie
let's try it out
401 unauthorized we did not get a cookie
i think we're running into our secure
site error where thunder client doesn't
let us test even though we need that
secure site option in there
for chrome so let's take this out
temporarily i'm in the auth controller
as we issue the refresh token
let's take this out and put it in a
comment near the end in production once
again you would want this back in there
however we can't test it with thunder
client with it in there so let's go
ahead and save now
and once we've got the server updated
let's go ahead and run the auth again
and then we should be able to test the
refresh and now we're up and running
connected to mongodb server running on
port 3500 let's try this again in
thunder client we'll go to the auth
first
hide this over here i'll go ahead and
log
and we've got a cookie let's go to the
refresh now
and come back before we were
unauthorized but let's see if this works
now and yes it does
our cookie works you just have to take
that secure out while testing with
thunder client so the refresh route is
working we have tested the register
route we have tested the auth route
we've tested the refresh route what is
left is the log out route so once again
we'll probably need a fresh login to
ensure we are logging out correctly so
let's go to the auth
and now let's see who we're logging in
it looks like walt won that's fine
we'll send that and now let's go to the
log out route
and
come over here in thunder client and it
looks like we actually don't need to
send anything it's just going to log out
the refresh token that's right
so we'll just send
and 204 no content that's fine we didn't
expect any content to come back but we
can see down here because we logged the
result
we logged out
there's the route we went ahead and
logged out and we logged out our user
walt 1 and we set the refresh token to
an empty string okay those updates using
the user model were not too bad at all
let's go back to the file and what we
want to do now is update the employees
controller
with the employee model and this will be
just a little bit more work than what we
had previously had but we'll start out
once again
by eliminating the json file and let's
go ahead and import our employee model
so we'll have const
employee and we'll set that equal to
require
and then we need to go up one folder
find the model folder and then employee
that is correct
and now let's look at our get all
employees route well before we just grab
the json and return that now this will
be just a little bit more
to do with
mongodb and of course this will be an
async operation so let's remove this
response json for now
and we'll define employees
and let's set this equal to
await
employee.find and by calling find like
this it will return all of the employees
then we can say if
there are no employees
we're going to return and then we'll
send a response
with a status 204 which is no content
and then
json and we'll send a message here
and in our json message we'll just say
it's a string no employees
found and after that we'll assume we do
have employees if we've made it this far
so we'll say response.json
and we'll send the employees and i can
see i've got an error i put brackets
instead of curly braces
go ahead and eliminate that bracket and
replace this one as well and we should
be good so let's go ahead and save this
now let's look at the create new
employee function which will also be
async so let's start here
and with that
we can probably go ahead and change
quite a few things that we have in here
so i'm just going to highlight all of
this
and remove it
and after that we'll start out with an
if statement but different than we have
below this will be if a request and now
we can use optional chaining to say as a
body
and then if it has a first name because
we need all of that
or
we can say the same thing
about the last name essentially we're
saying if there is no first name or last
name
and we would expect the request to have
a body but for some reason
if it doesn't we'll catch that as well
so if we don't have any of those
let's go ahead and send a response here
and we'll return
response dot status and this would be a
400 because it would be a bad request
there's json curly braces this time
message
and our message will be first
and last names
are required and you can see we had
something very similar below but it was
just a little different than that so now
let's eliminate that as well let's just
go ahead and delete these lines too and
we'll start over just a little bit and
put an extra line there and we'll do a
try catch block
and of course we have the catch after
there's our error and if we catch an
error let's just console
error the
error okay but in the try block
now let's define a result
and set this equal to
a weight
use our employee model and now we create
we'll create a record here
and we'll say
first name
is request.body.firstname
and lastname would be much the same
pattern
last request.body.lastname
and i'll put my semicolon here and save
but we still need to send a response so
we'll say response.json
oh let's put a status first
and the status here would be a 201 which
stands for created
and then we'll send the result back with
the record that we have created okay
let's scroll down now and look at the
update employee once again it will be an
async function
and after that we'll start out a little
differently so let's go ahead and delete
and
well we don't need to delete that line
let's just delete the top line to begin
with okay so we'll start out with if and
once again
request and let's use optional chaining
body and now we're looking for
an id
and if we don't have the id we'll
return response status
400
and after that we can just give a
message in json
and let's see if it's the same message
that we have below
not quite the same so here let's add a
little message different and we'll say
message
an id
parameter is required
okay so we've confirmed we now have an
id if we make it to the next spot
and here we can define our employee
so we'll say const
employee and set this equal to
a weight call our employee model and
we'll use find one here
and now remember mongodb uses an
underscore id it automatically generates
that and we're going to compare that to
request.body.id that is passed in as a
parameter
and after that we need to call the exec
at the end to call that function into
action now let's go ahead and delete the
next line here and we can say if there's
no employee
return status let's change this to a 204
because it's not really a bad request at
this point it just means that they
requested an id that doesn't exist they
might have
issued the request properly but now
let's say
something just a little different we'll
say no employee matches
and id and now let's get rid of this at
the end and get rid of the not found
part no employee matches whatever id was
passed in there
okay so we're sending our status 204
we'll save that much now let's look at
this if the request dot body and let's
go ahead and optionally chain
first name
and last name
and then we can say employee first name
equals the request.body first name and
last name is the same that's fine
so we're just optionally chaining that's
all we change there that's different
than we had before and now this gets
much simpler here we're not sorting any
json data or
creating new ids or anything there so
let's remove all of that and we'll just
say const
result equals
a weight
employee notice this is the employee
document we've created or we've found
actually we didn't create it's the
employee here not the employee model but
employee dot save
and now we've saved any changes we've
made to this employee document and then
instead of data employees
we'll just send the result we've defined
here
and save and that is our update employee
function okay let's scroll up and take a
look at delete employee which should
also get a little simpler but we're
going to start out much like we did
before so we'll say if there is no
request and will optionally chain
body and then we'll optionally chain the
id
and we're going to return response dot
status
400
and that should also be json
and here we'll have a message
and our json will say
employee
id required
and put the semicolon there and now we
need to define our employee again which
would be much the same as we had before
really it's the find one with the body
id so let's just scroll up here and copy
exactly what we had before
and come down
and here we have defined the employee id
again and so then our
employee not found will probably be the
same as well so we can copy that
and come down here as well and just copy
this over and paste
and then after that we will not be
filtering so let's just remove these
lines here once again
and we'll say const
result
equals
await employee dot delete one now
instead of find one
and here once again this will be
underscore id and then it will be the
request dot body dot id that was passed
in
we do not need an exec after delete one
and then we will put in
the result and if you're wondering why
we don't need the exec here it's just
all based on the documents so once again
look at mongoosejs.com
and those docs and you'll see which
different methods you need to put that
after and which ones you don't and
finally let's check the get employee
route as well and we need to put in a
check as we had before with the others
let's check the request if there's no
request
dot
params this time and then of course
we're looking for an id once again
and we have a similar message at the end
so let's just copy this over
and paste it in because that is required
and now we need to find an employee
again so we can copy the same line as
we've defined the employee before
and we'll come back down here and set
employee equal to the find one statement
if we don't find the employee well
that's a repeat of this i'll just copy
this return line here and paste it in
here
and then we're already set because we
just want to return the employee as we
were before as this is the get employee
route or handler just for the one
employee and of course we're expecting
that id parameter to be sent in so let's
save that and we have completed
our async
functions let's put async with that as
well and if we didn't with the others it
looks like i also forgot to do that with
the delete employee let me check the
rest
we need to have all of these functions
as async functions and they complete the
different crud operations create read
update and delete
for the employees collection before we
test the routes there is one error to
fix notice i was using params as we're
using a git route to pull that in but i
put request body dot id
in the function and this needs to be
[Music]
params.id
to access that value that we get from
the url in the route okay so now that we
have fixed this and we're now using
params let's test the routes and we need
a jwt to be authorized so let's go ahead
and use the auth route you might want to
change your code to timeout or for a
longer timeout duration than 30 seconds
mine is set to 30 seconds so i've got to
be a little fast to test these reps but
let's go ahead and get authorized
now we've got a new access token
we can jump back over here i'll open the
employees routes i want to get all
employees
and now for the authorization i need to
paste in the new token and we get our
one employee walt walters
i'm going to copy his id
open the terminal window
and just paste his id down there because
i'm going to need it to test the
individual get employee route
so we'll come back now and get
authorized once again
collapse the employees api
routes and check the authorization get a
new access token
once we've got the new access token
go back to the employees api go to the
get an employee route an individual
employee
and
paste that token in here and i need the
id to put in the url
i'll paste that in and send
and we get walt walters back once again
now that we're using the
request.params.id for this route because
the user id is read directly from the
url as it is a get request okay let's
get authorized once again and then we'll
come back and test the post
here is the auth route again we'll send
get a new token
back to thunder client open the
employees apis
look at the posted employee
here's the new
auth paste that in look at the body
we've already got walt walters let's
change this to dave
walters is good now we have dave walters
as an employee that was created a status
201 which is created
one more authorization to go so we can
test the put i guess two more and then
the delete as well
so let's go here and authorize once
again
new access token
come back to the put route
and let's see what we need here let's
put in the access token
and in the body oh we need to specify
the id once again well let's and we
change a first name to david
let's go back and i believe in the get
employee route i still have that id that
we used before
oh and it's still down here in the node
terminal as well so we can pull it out
of there so let's reauthorize
new access token
we will come back to the employees
use put
and for put we need to put in the new
access token
and then in the body we need the
employee id so i'm going to paste that
in here and
change walt to david
and now we have david walters and of
course there's already a dave walters as
well
so the put route works and now we want
to delete and of course we'll delete
this same user that we still have the id
handy for
so let's reauthorize one more time
get a new access token
we have the new access token
copy that go to the employees routes
delete an employee
paste in our new access token and in the
body we once again need the employee id
we paste in that id
and
we get returned the employee that was
deleted so david walters is no longer in
our collection but it was returned
by the delete route
as a confirmation that this employee was
deleted okay so from here what you
should do
is go ahead and
create a users js in the routes
and then api folder and allow some user
information to be requested maybe get
all users and maybe delete users and set
that up so it can be accessed in the
route by admin only such as
roleslist.admin because that's the users
and i'll let you do that on your own but
i will put it in the completed
repository that i will post the link
below so there'll be a link to the
starter code and then a link to the
completed code
as well but before i go
i want to show you how you can easily
launch a repository
and if that repository create or has a
node application like this rest api
it will easily be deployed okay there
are several places on the web where you
can host your node project and even a
rest api using express in like
we've created i'm going to show you one
of the easiest ways to deploy
a project like this and we're going to
deploy to glitch.com so i'm going to put
in another bold statement here and say
well not start let's just say
deploy by
clicking the
button
above
okay now that we've put that in i'm
going to copy and paste some code but
i'm going to just change which project
it was for so
this code will put a button on the
readme of the repository that i share to
glit or not to glitch to github
and then when the button is clicked it
will launch
on glitch so here we have to put in the
name of the current project and so i'm
put
async crud i believe i named this
project
and i'll save
and we can verify here by i'll go back
and let's see i named this async
crud that is correct
so
i'll open up the terminal window and
i'll push my changes to github that i've
already initialized repository so i'll
add the file
and i'll commit
messages updated
readme
and i'll push to
github and i need
a passphrase set up with mine and there
we go i've pushed that to github and so
now if we go back
and look at the readme
for this repository there should be a
button that's available and it says
remix this
and that will allow you to go ahead and
launch this application on glitch so
let's go ahead and do that i'll just
click the button it takes just a minute
to load the project
and it will be up and running except for
one thing and that one thing
is the values that we
have decided to put in our dot env
remember the dot e and v file should be
listed in your git ignore so it is not
here and notice the remix button is much
larger once it gets to glitch that's
interesting
okay so
once again looking at the get ignore you
should have your enb file here so the
secrets that you have stored in the dot
enb
need to go in the dot e and b that is
provided here at glitch
and you can of course click this to
learn how to use environment variables
we've already talked about that so you
can add a variable and here is where you
would type like
access
token
secret i believe is what we named one of
ours and then you would paste in your
value for that you would do the same for
the refresh token secret and you would
also want to do the same
for the
string you put in the connection string
that we used for mongoose and mongodb
but this is just an example to show you
how you can go ahead and launch that so
what i'm going to do is leave that
button
in the readme and i'll put a little
extra note in the readme saying remember
to add your own values in the dot env
because you can click that button
right there in my repository
and from there you can bring back up
this project
in glitch and if you create an account a
glitch you can save it there as well so
that will help you launch any future
node application you might create
and this would allow you to put a button
in your repositories and launch them as
well and i think you'll like working
with glitch also i use it for several
things and i should add that once you're
on glitch and you have your project
launched
it will give it a name like this and
then you'll have your live project you
can click change url here and you'll see
the url for your project so that would
be the url for your api
so that is what you would want to go to
for your api and then the
end points would go of course after this
if you're trying to pull up any specific
endpoint so we have covered a lot in
this series and of course there is much
more depth you could go into as you dive
into the docs but i feel like we've
covered the create read update and
delete
for and mongodb we've built a full
rest api
with node and express and we have added
user authentication we have talked about
authorization and
json web tokens as well to protect those
routes so if we talk about next steps
you would want to take what we have
built
and what we have built here on the back
end using express and node could
be combined with react
for the mern stack so if you haven't
learned react you want to check out my
nine hour beginners tutorial for that
and you can combine that knowledge with
this back end course knowledge to
complete the mern stack however you
could also
add whichever front-end technology you
wanted to this back end with
express and node for whatever
application you wanted to create
remember to keep striving for progress
over perfection and a little progress
every day will go a very long way please
give this video a like if it's helped
you and thank you for watching and
subscribing you're helping my channel
grow have a great day and let's write
more code together very soon
Can't find what you're looking for?
Get subtitles in any language from opensubtitles.com, and translate them here.