Teacher: In this video, let's now create
a better and more useful error class,
and also do some refactoring.
And starting with that error class,
let's create a new file in our Utilities folder.
So, new file, and I'm going to call it AppError
because that's going to be the name of the class, all right?
So, class AppError, and we actually want all of our
AppError objects to then inherit from the built-in error,
and so let's actually extend the built-in error class.
So, we used extends Error, okay?
So, we did that before somewhere in this course,
and again this is just ES6 classes, all right?
In this case, class inheritance,
where one class inherits from the other, okay?
Then, as always, our constructor,
and what we're going to pass into a new object
created from the AppError class is going to be
the message and the statusCode,
so just these two.
Okay, so, remember the constructor method is called
each time that we create a new object out of this class.
Now, as usual, when we extend a parent class,
we call super in order to call
the parent constructor, all right,
and we do that with message because the message is actually
the only parameter that the built-in error accepts.
Okay, so this is basically you're, like, calling error,
all right, and then this is here is the usual stuff,
we set the statusCode
to statusCode, all right,
and now we also want to set the status itself.
So, remember the status can either be fail or error,
and we could pass that into the object,
but it's also not really necessary,
because the status depends on the statusCode.
So, when the statusCode is a 400,
then the status will be fail,
and if it's a 500, then it's going to be an error, right,
and so that simply tests if the
statusCode starts with a four.
So, in JavaScript, there is a startsWith method
that we can call on strings,
and so I think that's the easiest way of doing this test,
and so that's basically convert the statusCode to a string,
and for that I'm simply using a template string with
the code in there, so this,
or actually we can just use statusCode.
So, statusCode, and then startsWith,
then here we also need a string,
and so if the statusCode as a string
starts with a four, well, then we have a fail.
And so here, let's use the ternary, okay,
and so it's fail when it starts with a four,
and otherwise it's an error, okay?
So, very simple, and this already saves us
from manually have to pass in either fail or error.
All right, now next up,
all the errors that we will create
using this class will all be operational errors.
So, errors that we can predict
will happen in some point in the future,
like for example a user creating
a tour without the required fields, right?
So that is an operational error, okay,
and so again, from now on, we will
always use this AppError class here
that we're creating right now
in order to create all the errors in our application.
And so these errors will be operational errors,
and so what I'm gonna do now is
to actually also create a .is operational property here.
So this.is operational,
and set it to true.
So all of our errors will
get this property set to true,
and I'm doing that so that later
we can then test for this property
and only send error messages back
to the client for these operational errors
that we created using this class.
And this is useful because some other
crazy unexpected errors that might happen
in our application, for example a programming error,
or some bug in one of the packages
that we require into our app,
and these errors will then of course
not have this .is operational property on them,
all right, and it might sound
a bit confusing at this point,
but don't worry, it will make
a lot of sense when we then
implement that part that I just mentioned.
So, testing for this .is operational property here.
All right, and now just one last step
is that we actually also need to capture the stack trace.
Now, what do I mean by stack trace?
And so, let me actually show that to you,
so, show you what stack trace is,
and so I will log it here to the console.
Console.log and error.stackTrace,
so each and every error gets this stack trace,
and actually it's just stack, okay.
So err.stack will basically
show us where the error happened.
So let me run this here now,
and so let's take a look at this,
and so it gives us here the error
and then also where it happened, okay?
So, in this case here, of course,
add app.js at line 32.
So, right here.
So, that's where we created this error,
and so it's now in our stack trace,
and it also shows us the entire call stack here,
which in the end the originated in this error, okay?
So we kind of want to preserve that
and also at the same time not add this method here,
or this class, to that stack track, okay?
And again, that sounds a bit confusing,
I know, but in this case
it's not really important, all that matters
is that we just add this line of code here,
which is Error.capturestackTrace,
so exactly what you get here,
and at first we need to specify the current object,
which is this, and then the AppError class itself,
which is gonna be this.constructor.
Okay, and so this way when a new object
is created, and a constructor function is called,
then that function call is not gonna appear
in the stack trace, and will not pollute it.
All right,
so let's put it down here,
and yeah, so that's our AppError class,
and so let's now actually export it from here.
So, module.exports is of course the AppError.
Great, just one question that you might have is
'Why didn't I set this.message equal to message?'
Well, that's just because right here I called
the parent class, right, and the parent class
is error, and whatever we pass into it
is gonna be the message property.
So just as I told you before.
And so, basically, in here by doing this parent call
we already set the message property
to our incoming message.
All right, give it a save, and now we can close it,
we no longer need it,
and here we're just gonna import it here.
Const AppError,
require,
and then it's in utils,
so Utilities, and appError, okay,
and so now let's actually go ahead and use it.
Comment out this piece of code,
and actually delete the one that we had before,
and that will now create the error
right here inside of next.
So, new AppError,
and then the message,
which is this one,
and the status code.
Right, and the fail, remember, will then automatically
be figured out, and so let's delete all of this,
give it a save here,
and one more time, test out this wrong route,
and indeed we still get our same error,
and we also still get our exact same stack trace.
Okay, and finally I actually also want
to export this middleware here, okay?
So basically, this handler
because throughout the rest of the section,
we're gonna build a couple of different
functions for handling with different types of errors,
and so I want all of these functions
to be all in the same file, all right?
And we can say that all of these functions
that I just mentioned are handlers, okay,
and so handlers, we also call them controllers
in the context of the MVC architecture,
and so let's now actually create
an error controller file in our controller folder.
Okay, and this might sound or look a bit weird
because we actually do not have an error resource
okay, and so probably some people are gonna disagree
with me that this is the correct place,
but I personally like to do it like this
because at the end of the day,
these functions, they kinda are like
really for controlling our errors, all right,
and so for me at least it makes sense
to simply call this function here the error controller,
and here I wanted to paste that middleware function,
but I guess I didn't copy it, so let's do that again.
All right, and so actually I'm gonna export
this one here as the module.exports
because the other handle functions
that we're gonna create later on,
I will not export them from here.
So they are just kinda just gonna be helpers.
So, module.exports equals this function, okay?
Let's actually get rid of this console.log here,
give it a save, and now back into our app
we of course now need to plug in
that middleware function here.
So, let's again go ahead and import it
and I can call it whatever I want,
and so let me call it
the globalErrorHandler
with a capital H,
and I'm gonna require
controllers and errorController.
Put it back here, and now for the final test
after this refactoring.
Let's see, and indeed
one more time, everything works just fine, okay?
So, perfect, that was the goal for this video,
see you in the next one.