Instructor: In this lecture, let's implement a better way
of catching errors in all our async functions.
So right now, in all our async functions,
we have these try catch blocks, right?
So, remember how we added to all of these handle functions
this try and then this catch block, okay?
And so all of them have this
because all of them are async functions
and that's how we usually catch the errors
inside of an asynchronous function.
So using a try catch block.
Now that really makes our code look messy and unfocused
so the goal of this createTour method here, for example,
is to just run this code.
We actually do not want to mess around
with error handling here,
so right now we have these two extra blocks,
try and catch, and with that comes
this extra indentation here
and so again, this really makes our code look not clean.
It's really not focused.
Also, we have a lot of duplicate code here
because in each of these handlers,
we have quite a similar catch block.
So in all of this, all we're doing is
really to send this response.
And that response would actually not even be sent here
but instead in our global error handling middleware,
remember, but that's another topic.
For now, what I'm trying to focus here
is that these try catch blocks inside
of each of our async functions are not ideal
and so let's now try to fix that.
And the solution is to basically take the try catch block
out of here and put it on a higher level
in another function.
So basically what we're gonna do
is to create a function,
and then wrap this async function into that function.
All right?
And so let me call that one catchAsync.
So catchAsync, and I'm calling it this way
because the goal of this function
is to simply catch our asynchronous errors.
So simple, right?
So into this catchAsync function,
we will pass in a function, okay?
And remember, the function that we're gonna pass in
is basically this one.
So now that we're working with this example
the function that we will pass into the catchAsync
is this function because this is where we want
to catch the asynchronous errors.
So let's actually do that.
So fn, and then wrap that entire function into there.
So next up, what we then have to do
is to actually call this function in here.
So call it, and this function should receive
request, response, and in fact, also next.
So this one here should actually also have next
and all the others, as well,
because remember, we need the next function
in order to pass the error into it
so that that error can then be handled
in the global error handling middleware.
So all of these functions are gonna need
request, response, and next.
But let's leave that for later.
For now, let's really focus on our catchAsync here.
So this function here that we passed into async,
which is now called fn up here,
is an asynchronous function.
And remember that asynchronous functions
return promises, right?
And when there is an error inside of an async function,
that basically means that the promise gets rejected.
And so up here, where we actually call that function,
we can then catch that error.
So we catch it here, instead of catching it
in the try catch block.
So catch, and error, and then next, and pass the error.
And we can simplify this,
but for now let's remove the try catch block here,
and so simply focus on this piece.
So this is all we want
and we no longer need the try catch block,
again, because that catch is now basically transferred
to here, to this line.
It's no longer a catch block,
because in here it's just easier to use
the promise that the fn function returns.
So again, this fn function is this function,
so the one that we passed into catchAsync,
and so that is gonna return a promise.
And actually, I noticed here that this, of course,
should be called catchAsync, all right?
And then so that function that we passed
into catchAsync, this one is of course then fn.
All right?
So the argument that we passed in there.
Now there are actually two big problems
with the way that this is implemented right now
and so this way, it wouldn't really work at all.
So first one, this function call here
has no way of knowing request, response, and next.
We did not pass them into catchAsync here,
and so really there's no way
for the function to know the values of these parameters.
And second is that right here
we are actually calling the async function.
And to see this a bit better, let's just
get completely rid of this code.
So here we have catchAsync and we are then calling it
using the parentheses of course.
And then inside of catchAsync we are also then
right away calling the fn function,
and that's not how it is supposed to work.
So createTour here should really be a function
but not the result of calling a function.
But that's right now what's happening.
So right now catchAsync is being called,
which then calls this function here, okay?
And so again, this function should not called,
but instead it should sit here and wait
until express calls it.
And express will of course call it
as soon as someone hits the route
that needs this control function.
And so the solution to that is to basically make
the catchAsync function return another function
which is then gonna be assigned to createTour
and so that function can then
later be called when necessary.
So let's do that here.
So let's return an anonymous function
and so remember that this is the function
that express is then gonna call.
And so here is where we then specify
request, response, and next.
Actually missing the arrow here.
And all right.
And that's actually it.
That's our catchAsync function.
Just remember how I said that we could simplify it
and that's just because, in JavaScript,
we can simplify it like this
so all we need to pass here is the function,
and it will then be called automatically
with the parameter that this callback receives.
So it's the same as writing next.
So let's recap here.
So in order to get rid our try catch blocks,
we simply wrapped our asynchronous function
inside of the catchAsync function that we just created.
This function will then return a new anonymous function,
which is this one here,
which will then be assigned to createTour.
And so basically it is this function here
that will get called as soon as a new tour
should be created using the createTour handler.
And so that's why it has the exact same signature here
as this async function, with request, response, and next.
Now what this function here will then do
is that it will call the function
that we passed in initially,
so this one here,
and it will then execute all the code that is in there.
Now since it's an async function here,
it will return a promise
and therefore, in case there is an error in this promise
or in other words, in case it gets rejected,
we can then catch the error that happened
using the catch method that is available on all promises.
And in the end, it is this catch method here
which will pass the error into the next function
which will then make it so that our error
ends up in our global error handling middleware.
So this here, this line of code,
is really where all the magic happens,
and this is, in fact, what allows us
to get rid of the catch block that we had previously.
And a note:
this is quite complicated to wrap your head around
and in case that it's not 100% clear,
don't worry, it's not all that important.
This is really just about figuring out how exactly
asynchronous code works behind the scenes in JavaScript.
So it's not really so much about express or Node.js,
this is more about how asynchronous code works.
Now anyway, if we now create a new tour
and some error happens,
for example, from an invalid input,
then that error should of course be catched here
in this catch function,
and will then be propagated to our error handling middleware
and so that one will then send back the error response
that we're expected to receive.
And so let's now actually try that out.
So create a new tour.
Here we have the body,
and let's now get rid of one of these required fields.
All right.
And so that should trigger an error, and so let's see.
And indeed, our tour validation failed,
which is exactly the error message
that we were supposed to receive.
Now here, you see this 500 Internal Server Error,
which is simply due to the fact that right now
the error that was propagated
to the error handling middleware
did not have any status code specified
and so remember our default is 500
and so that's the one that was then sent back.
And of course, we need to fix that
and we will do that in a later lecture in this course.
For now, what matters here is that first,
our catchAsync function works just as intended,
and second, that the error was actually propagated,
so it was catched by our global error handling middleware
which then sent this response back to the client.
Now let's actually go ahead and export
this catch function into its own file.
So I'm grabbing it from here
and I will create just another file
into our utilities folder.
So catchAsync.js.
And to here, module.exports should be just this.
And so that should work just fine.
Back in our tour controller we of course
now need to import this function.
And as usual, we need to go up one folder
and then into our utilities folder
and then catchAsync.
All right, and now all we need to do
is to get rid of all these catch blocks
and wrap all the handlers into the catchAsync.
So let's do that.
First do the wrapping, and then getting rid of these blocks.
Give it a save to reformat, and here we go.
And now it's simply a process of repeating this
until all of the tours, or,
sorry, until all of the handlers are done.
Okay, quick save.
This one is already done.
Next up is update.
And only one more missing.
Or actually, that's not true.
Down there we have even more async functions.
So we'll get rid of the catch and the try here.
And I believe this one is the last one.
And yeah, indeed it is.
All right, now one other thing that we need to do
is actually add next to all of them here as well.
But of course, not in uppercase.
Okay.
And let me actually just select all of them here
so that I only have to write it once.
And so that added it to all of them.
And so now I guess we're done with this part.
Let's just very quickly test it again,
not with this one, but here with,
not with this one as well, so we're done.
And I'm gonna try to get a tour
but with an invalid ID, so let's see what happens here.
And indeed, we get our error message
and again with the 500 status code,
which is not correct, remember,
but we're gonna take care of that
at a later point in time.
So right now, let me show you
that this here is where the 500 comes from
because right now there's no status code
inside of the error that we get
because these errors, they actually come from Mongoose
and so we have no way of adding
a status code to these errors.
Or actually, of course, we could do it,
but that would just be even more confusing
and so we're gonna find another way
later in this section.
For now, I'm really happy with the result that we have here
and so that's it for this lecture.