[MUSIC]
We had already developed a full-fledged REST API server using Express and
MongoDB as part of this course.
In order for the server to be able to communicate meaningfully with our client,
we will make a few minor adjustments to it so that it returns the right kind of data.
So that our client implementation can work more efficiently
with the data returned from our server-side.
So to do that,
let's work on a few adjustments to our server-side in this exercise.
Going to our server in the editor, before we start working on the server,
I would suggest that you download all the images that I have provided
as a zip file in the exercise resources called images.zip.
Unzip the file and then obtain all the images from there, and
then copy those images into the public images folder in our server.
So you see that here I have copied all the images into my public images
folder here already.
Because our server is the one that is going to serve up all these images for
our client application.
Next, we go to the cors and adjust that whitelist, here.
So for our Angular client application, if we start it up
with the ng serve command, it runs at localhost:4200.
So any requests coming in from our Angular
application to the server will carry that as their origin there.
So that is why I'm going to add that into my whitelist, so
that any cors problems will be addressed automatically.
So going into that cors.js file,
in my whitelist, here, let me add in
the http://localhost:, 4200.
And this is the origin from which all my Angular client will be
originating the request that come to this server.
So that way, my cors will not cause problems for may Angular client.
Next, we need to update a few of the routes to handle
the query parameters, and also a few minor other adjustments.
Let me start with the promoRouter.js files.
So that is simple to get updated.
So going to the promoRouter.js file, in the promoRouter file, for
the get request that we get here,
instead of doing Promotions.find with an empty JavaScript,
here I would supply the req.query as
the parameter to that Promotions.find here.
Same thing with the leaderRouter also, so let me go to the leaderRouter.js file.
And in the leaderRouter also,
here where you find Leaders.find with the empty JavaScript object.
Instead, replace that with that req.query, so
that the query parameters will be passed in.
So this is where we will pass in the featured column
through as the query parameter.
Now having adjusted these two, let's now work on the dish route,
so going to dishRouter.js file, even in the dishRouter, the same update here.
So going to this Dishes.find in the get method here, change that to req.query.
So with this, my dishRouter update has been completed.
So we have now updated the promoRouter, the readerRouter, and
the dishRouter, then we will move on to update that favoriteRouter.
Now you would have implemented the favorite router as part of your
fourth assignment.
Now in the favoriteRouter, you will see that for
the favoriteRouter I'm not showing you the remaining parter because that
you should have done as part of your assignment.
Let me first draw your attention to the get method for
the favoriteRouter.route("/:dishId').
Now, I'm going to use this get method to just
check to ensure that the specific dish with
a dishId is already a favorite for the user.
So instead of simply saying GET operation not supported,
I am just going to leverage the presence of this get operation.
And then we'll say,
Favorites.findOne.
And I will say,
user: req.user.id.
So we will find the favorites for that particular user.
And then we'll say, .then, favorites.
And, catch((err),
next(err)).
And then similarly here, we will add in the error here.
Next(err)), here.
So inside this .then, when we get the favorites,
then we will check, if (!favorites).
So, if there are no favorites for this user,
then obviously the dish that we are checking for doesn't exist.
So we'll return, res.statusCode, 200.
SetHeader( 'Content-Type
' 'application/json').
And then we'll return,
Saying, "exists": false.
So what we are specifying here is that if the get is done
to this endpoint with a dishId, this exists flag here will
be set to true if this dish is part of my favorites.
If this dish is not part of my favorites, I will set the exists flag to false.
So here, you see that since I don't have any favorites, so
exists should be automatically false at this point.
So we'll say "exists": false, and then we will return the favorites, here.
Well, obviously in this case,
the favorites would be null at this point, otherwise.
So which means that favorites is not null, so we'll say if,
(favorites.dishes .indexOf),
so we are going to be searching for req.params.
DishId, so we're going to search this.
Favorites.dishes.array, to see if does dish exists in there.
And if it doesn't exist, obviously this will return a negative index here.
So in that case also, I will return exactly the same as here.
So if it returns a negative index,
then that means that even though this particular user has a center favorite.
This specific dish doesn't exist in the list his or
her favorite, so that's why it'll return exist false here.
Now otherwise that means that the dish exist in the list of favorite.
So in this case, I will return status code 200 exists is true.
So that way when the user performs a get operation on
this end point with the /favorite/dishId.
Where the dishId is supplied as the parameter here.
As the records parameter here,
then we will check to see whether that dish exists in the favorites.
If none of the favorites exist for this user then we'll return an exist false.
Similarly, if the favorites exist,
but that particular dish doesn't exist in the favorites then it'll return a false.
Otherwise it will return a true.
So this exist flag can be used by my client to check
to see if this dish is part of the list of favorite dishes for this user or not.
Finally we'll update the users.js file.
Now in the users.js file, we need to add in
another router.options field here because
sometimes a Post request as you saw with the login,
we'll send the options first to check, especially with cars,
whether the post request will be allowed.
So that's why they'll check cars, cars with options here and then.
We'll simply return
A status message of 200 in response to that.
So for any endpoint under users if we received
the options will simply return a status 200 here.
Now the Login function that we had implemented earlier.
We have simply done passport.authenticate('local') here and
then we check for the remaining values here.
Now this passport.authenticate('local'), if the user doesn't get
authenticated it simply returns an unauthorized in the reply message.
Now that may not be very meaningful for
the client side to display this information.
So that is why we will enhance this router post login method
such that the authentication will return more meaningful information at this part.
So to do that, we are not going to be doing the passport
authenticate inside in here.
Instead, we will take, so let me remove that from there,
and then you will see how I will update the router post here.
So we'll see carsWithOptions.
And then we'll include the req, res and next here.
And inside the req, res, and
next here, passport.authenticate,
We'll call this with 'local'.
Now when we call this with local, if a authentication
error occurs the passport.authenticate can be made to return the error value,
and also it'll return the user if there is no error.
And it could parameter called info,
which will carry additional info that might be passed back to the user.
This error will be returned when there is a genuine error that occurs in
the possible to authenticate.
But if the user information is sent in to passport.authenticate but
the user doesn't exist, then that is not counted as an error.
Instead, it will be counted as user doesn't exist.
And that information is passed back in the info object that comes in.
So the error will be returned when there is a genuine error that occurs during
the authentication process.
But the info, they'll contain information if the user doesn't exist and so
the passport.authenticate is passing back a message saying that the user doesn't
exist or either the user name is incorrect or the password is incorrect.
And so on, so that information will be passed in, in the info message.
Now, we will see how this is useful to us when we look at the client-side a little
bit later.
So in this situation,
we will handle this as follows.
And not only that, when we call this passport authenticate,
we also need to pass in a (req, res,
next) as that three parameter strip.
So this is the structure.
When you need to call passport authenticate,
expect it to pass you back information like this as a call back method here.
So you also need to supply this req, res next right there too.
Now, if this occurs.
So we'll say if (err).
So if there's an error that occurs,
we'll say return next (err) and then let the error
handler of our express router take care of that.
Now, we'll consider other situations.
If(!user), so if we have reached this point then that means that it
was not an error that occurred but instead perhaps the user could not be found.
If the user is not found then the user here will be set to null in this case.
So, in that situation if the user does not exist,
then we need to be able to pass information back to the server side.
So what we will do is we will pass in this information in this format,
so I'm going to cut this out from here, and
then paste it in here and then we will edit so.
If the user is null, then we will send back a statusCode
of 401 which means unauthorized, and
then we will send back information success, false.
And then we'll not be passing back the token, we'll pass back the status message.
So here we'll say login, Unsuccessful.
And then the third information will pass this info,
that object that we received here as the third part in
that reply message that we send back from our server here.
So the success flag will reset to false.
The status will be set Login Unsuccessful and the error information,
that is passed in the info will be passed back.
Now note that this situation will occur if either the username and
the password is incorrect.
And so this is not an error, in the error sense, but the fact that the authenticate
could not find either the user or the password of the user is incorrect.
So that information will be encoded into the inflow that comes in, and so
that I will pass back as an error to my client side.
Otherwise part is
handled as req.logIn.
So if this is successful,
the passport.authenticate will add this method called req.logIn to the user.
So at this point we will just pass in the user object that we've obtained.
So here, if we have reached this point,
then that means that the user object is not null and
also no error occured, so that means that
the user can be logged in and so we wil say err.
So it will try to login the user.
So we'll say if err.
And then we will pass back that same kind of error information that we did here.
So in this case, if the error.
If error, then we'll pass back status code 401 and
we'll say success is false and status is Login Unsuccessful.
And then the error information
we will pass this as instead of
info we'll pass in could not log in user.
So that is the message that will pass back here, if the error occurs.
Otherwise, we would be down below here.
And so at this point we would be able to generate the token.
So if we have reached up to this point that means that the user has
successfully logged in and so we can now generate the token.
So we will generate the token based upon the user ID and
then we will pass back that token back to the user.
So here we will say var token.
And then we can say res.statusCode = 200, and then res.json(success) is true,
which means that the user successfully logged in.
And so the status would be Login Successful, and
then the third part instead of the error I will pass the token back to the user.
So we'll say token, token,
this token which we just obtained earlier, so that to token will be passed in
as the token property of the rip light message.
So here, notice that this axis is set to true.
So which means that the user successfully logged in.
And so the user can proceed forward at this point.
And this has to be done inside the req.logIn here.
So at this point we will close the req.logIn.
So notice that this is inside this req.logIn here.
So in there we will pass these three back.
So let me just indent threse three lines in.
And so that is how we would handle the users login.
So again, reviewing this code one more time.
So we'll do router post, but
instead of doing passport authenticate right there, we will say req, res,
next and then inside here we will do a passport.authenticate for the local.
And this authenticate will pass back, so we can supply a callback function to that.
And this callback function will return either the error, the user, or
the info here.
And so, if it does an error we'll just allow the express error handler to
take care of that.
If the user is null then that means that the user logon was unsuccessful,
and the reason for that will be in the info so that will pass back as
the info error in the plan message here.
If we come up to this point then the user is successfully verified.
So then we will login the user.
So the passport.authenticate will add in this method called logIn to
the request message, so we can call the login with the user and
if this returns an error, then we will return the error here appropriately.
If not then we would have reached the point where the user is successfully
authenticated so they can generate the JSON web token here and return the JSON
web token to the user to confirm that the user is successfully logged.
So that is the set of steps that we're going to use here.
Now the reason why I do a more elaborate way of handling it is that I want to
distinguish between the situation where a genuine error occurs during
the application process, as opposed to the situation where the user name is invalid,
or the password is invalid.
So those two cases will be handled by this situation where the info will
carry the information back to us.
So that's not an error perse but that's a situation where
the user is not a valid user or the user's password is not valid.
So that's how they will handle the user's log in process.
In addition, I will add in one more method here called,
checkJWToken.
It is quite possible that while the client has logged in and
obtained the JSON web token, sometime later, the JSON Web Token may expire.
And so if the user tries to access from the client side with an expired token
to the server then the server will not be able to authenticate the user.
So at periodic intervals we may wish to cross check to make sure that the JSON
web token is still valid.
So that is the reason why I am including another endpoint called
checkJWTToken, so if you do a get to the checkJWTToken.
By including the token into the authorization header,
then this call will return a true or false to indicate to you
whether the JSON web token is still valid or not.
If it is not valid then the client side can initiate another login for For
the user to obtain a new JSON web token, if required.
So to do this, we'll say cors, corsWithOptions and,
req, res, here, as expected.
And in here we'll say,
passport.authenticate('jwt',
And, {session: false}).
And this would return err, user, info.
So recall how we used the Passport authenticator earlier.
So the JWT session falls here.
So earlier here we say, passport.authenticate local.
So this is for the local authentication.
So this is for the JSON web token authentication.
So when we call that, then, since that is going to verify the JSON web token,
so I'm going to say, if (err), return next(err).
And then let the Express error handler take care of that situation.
And then we'll say, if (!user),
If the user doesn't exist, then similarly, else.
So which means that if the user object was found from the JSON web token and
then loaded onto the req.user, then that means that the user is a valid user.
And so can be allowed to proceed forward.
Otherwise, we're going to say that the user doesn't exist.
So we need to infer that the JSON web token has expired.
So at this point, we'll send a res with the status code of,
401, so unauthorized.
SetHeader(, 'Content-Type',
'application/json').
And then we'll return res,
.json, we'll say, {status:,
'JWT invalid!'.
And then We'll return a flag called success: false.
And then, We'll return the info that we
obtain if the user is not authenticated as the error at this point.
Otherwise, that means that we reach this point when the user is a valid user.
So in this case, let me just copy that code here,
we'll be sending a status code of 200, and
then the res.json will send JWT,
valid!, and success will be true.
And the third part, we will send the user's information.
So that way, when this endpoint is called
with the get method, then this will verify whether the token is valid or not.
If the token is valid, then you will receive this reply.
And from the success flag in the reply,
you can check to see whether the JSON web token is valid or not.
And this is useful on the client-side.
Now this passport here, passport.authenticate here,
will have to be supplied with req and res as the two parameters here.
So that's how we call that passport.authenticate.
Notice that whenever you call passport.authenticate and
expect this callback function in here, you need to append this point, req,
.res, to that passport.authenticate, and then back here.
So with this, we have updated everything on the server-side.
So let's save all the changes on the server-side.
So now our server is all ready to
handle the incoming requests from our Angular client.
With this, we complete this exercise.
In this exercise, we have now prepared our Express server to
handle incoming requests from our Angular client.
In the next exercise, we're going to look at the Angular client in more detail to
understand how it is communicating with this extra server.
This is a good time for you to do a Git commit with the message,
integrating client and server.
[MUSIC]