Instructor: All right!
And now to finish this section,
let's learn how to catch uncaught exceptions.
But what exactly are uncaught exceptions?
Well, all errors, or let's also call them bugs,
that occur in our synchronous code
but are not handled anywhere are called uncaught exceptions.
And like before, so just like with the unhandled rejections,
we also have a way of handling uncaught exceptions.
So let me quickly show you
an example of an uncaught exception.
So let's simply pretend that we're trying
to console.log something that doesn't exist.
And so right away, we get this error here, x is not defined,
and then by default, we get this entire stack trace here
printed to the console.
All right.
And now, to fix this, this is very similar to doing
the unhandled rejections.
So again, we're listening to an event,
this time it's called uncaughtException.
And of course it needs to be in quotes.
And then just like before, we pass in our callback function.
Okay.
And now actually what we're doing here is very similar
to what we did here.
So we wanna lock the error to the console
so that it then shows up in the logs in our server, okay?
So, giving us a way of then fixing the problem.
And then we want to gracefully shut down the server.
So let's, well actually here I want
to print the entire error to the console,
and not just the message, okay?
And let's also change the order here,
but that's it.
So the rest is the same.
And here it's of course
UNCAUGHT EXCEPTION!
Now I could refactor this here into a nice function,
but let's just keep it like this, all right?
Okay, so let's now rerun this.
And so let's take a look at what we have here.
And indeed, we now see UNCAUGHT EXCEPTION!
Shutting down.
All right?
And actually this again prints the entire stack
to the console, but I don't really want that.
I don't like that.
So let's just actually go back to what we had here.
So err.name and then err.message.
Okay, and so now we have the ReferenceError
that x is not defined, okay?
And let's just put the same here.
So just like it was before.
All right.
And that's actually all there is
to catching uncaught exceptions.
Now while here in the unhandled rejection,
crashing the application like we did here is optional,
when there is an uncaught exception,
we really, really need to crash our application
because after there was an uncaught exception,
the entire node process is
in a so-called unclean state, all right?
And so to fix that, the process need to terminate
and then to be restarted, all right?
And again, in production,
we should then have a tool in place
which will restart the application after crashing.
And many hosting services already do that
out of the box, all right?
So, completely automatically without
us having to do anything.
Now, in Node.js, it's not really a good practice
to just blindly rely on these two error handlers
that we just implemented here, okay?
So ideally errors should really be handled
right where they occur.
So for example, in the problem connecting to the database,
we should of course add a catch handler there
and not just simply rely on the
unhandled rejection callback that we have here, okay?
And some people even say that we shouldn't use these at all,
but I disagree with that.
I think that as a safety net, let's say,
they can be very useful and play a part in our application.
Now actually this handler here
should be at the very top of our code, okay?
Or at least before any other code is really executed.
Because watch what happens if I move this line of code here
before this handler.
So if I put it here, for example,
and then give it a save,
then you actually see that our handler
does not catch this exception, so this error here, right?
And so that's because only here at the end
we actually start listening for an uncaught exception.
But in this case here, the uncaught exception
happens before we even listen to that event.
And so therefore, we have no way of catching it, right?
And so we should ideally put it here, right at the top,
again before any other code executes.
Especially the one in our application.
So let's put it actually right here.
Now the problem here will be
that the server is not defined at this point.
But that's not a problem,
because actually we don't need the server here
at all, all right?
And that's because these errors,
so these uncaught exceptions,
they are not gonna happen asynchronously.
So they are not gonna have anything to do
with the server actually.
So we don't need this here at all.
We can just save it here,
and so now we have it before we actually require
our main application.
And so if we now have an error,
let's try it first here,
so you see that now we're back
to actually catching it in our error handler.
But if we now had this code, for example inside of app.js,
let's say, it doesn't really matter,
let's just say it's here,
and if we now run this, well then you see
that we're still catching that exception
in our error handler, which before would not be the case.
All right?
And now just for an experiment,
what do you think if we put this code
right here in this middleware function?
What do you think is gonna happen
when we save this file now?
So x is still not defined anywhere,
but let's take a look at what happens
when we now run this code.
And so now we actually have no error.
Or actually we do, but that's only
because of our unhandled rejection,
which is due to the fact
that our password here is still wrong.
So if we save that and now save it again,
you should see no error at all.
And indeed, we don't.
And why is that?
Well, it's because this middleware function here of course
is only called as soon
as there actually is a request, right?
And so let's see what happens when we do a request.
And so let's do a Get All Tours request.
And let's wait for it.
And, here we get a "Something went very wrong!"
error message with the 500,
and that's also because we are still
in production mode, okay?
But anyway, getting this message here, remember,
means that we have a nonoperational error here.
So basically an error that we did not create ourselves.
And so, right this, we now enter here
in our errorController, we go right here, right?
So into this else branch here in the sendErrorProd function.
And actually you see the error
down here in the console as well, okay?
And so that's what happens when there is an error
inside of any Express middleware, right?
So Express, when there is an error,
will automatically go to the error-handling middleware
with that error, okay?
And so that's why when we get an error
here in any of our middleware function,
it will immediately go here into this handler, okay?
And since we're in production,
it will enter this block here,
but since it's not a CastError,
and not this error, and not a ValidationError,
then as soon as the error is actually sent,
right here, we then enter this block.
And again, that's why we send this generic error, all right?
Let's now actually exit this mode here
and go back to our normal npm start.
And so right now, our error will be sent like this, right?
So send development error will give us all the details
about the error that is happening.
And so now indeed we get ReferenceError: x is not defined.
Okay?
So in app.js, line 21.
And so, line 21 is where it is,
but let's get rid of it.
I just wanted to show it to you.
Okay?
Anyway, with this, we actually close
our error handling section.
And there could be an entire course on error handling
with Node and Express, because it really is
an insanely complex topic.
But, in this section, you learn the essentials
that you need to know in order to really get started
building amazing applications.