Jonas: Many real world applications
have two special modules that are completely independent
of the rest of the architecture.
And these are a module for the project configuration
and also a module for some general helper functions
that are gonna be useful across the entire project.
And so let's now implement these modules in our own project.
And let's start with the configuration module.
So let's maybe close this one here first.
And then in our JS folder,
we create a new file
config.js.
Okay.
And so here into this file,
we will basically put all the variables
that should be constants
and should be reused across the project.
And the goal of having this file with all these variables
is that it will allow us to easily configure
or project by simply changing some of the data
that is here in this configuration file.
So therefore the name of config.
So of course we will not want to put all the variables
here in this file.
The only variables that we do want here
are the ones that are responsible
for kind of defining some important data
about the application itself.
So one example of that is for example,
the API URL.
All right.
So this URL here, we will actually reuse in multiple places
across this project, for example, for getting search data,
and also for uploading a recipe to the server.
And so all of them will use this URL.
But then imagine that at some point the URL needs to change.
For example,
because there might be a version 3 at some point.
And so as always, we don't want to change that everywhere
and simply have a variable which contains this
which we can then reuse.
So let's cut this here
and you might argue that we can
also simply create a variable here in the model,
but then we would have all these configuration variables
spread across multiple modules,
but it's way easier to simply have all of those variables
in one central place.
And so that's what we're going to do here.
So let's call this one API URL,
and then paste that here.
Okay.
And now all we have to do
is to export this constant from here.
So, I'm using uppercase here,
because this is basically a constant that will never change.
And so using uppercase for that kind of variable
is kind of a common practice
especially in a configuration file like this.
So, API URL is one of the variables
that makes a lot of sense.
And let's actually go to our documentation page
because indeed, as I mentioned before,
we are going to use this URL for this
and also for getting recipes.
So then we will simply have to add the slash ID.
Another great candidate for being a configuration variable
is going to be this API key
that we will generate a bit later.
So let's leave that for now
and go back here to this API URL.
And now actually import that here into the model.
So import, and now remember that here
we have a named import,
and later we will actually also have more named imports.
Okay.
And so here I could go ahead
and import all of them basically at the same time
and then as config,
but actually I prefer to import them one by one,
basically with their actual name.
And so remember we do that by using curly braces
and then the name
of the variable like this,
and then config is in the same folder.
And so that should work,
and here, all we have to do now is to replace,
or to actually put that API URL
right here.
So, if we save this now, then our application,
well, let's see.
And yeah, it is back to working.
Right?
And so we successfully basically export it,
this configuration value that we had here
into this config file.
Okay.
Next up, let's create a new file for some helper functions.
So this one is usually called helpers.js.
And the goal of this file or of this module
is to contain a couple of functions
that we reuse over and over in our project.
And so here in this module
we then have a central place for all of them basically.
And to start one great candidate,
is to actually create a function which will get JSON.
So basically a function which encapsulates all of this,
and also some of the error handling.
Alright, so let's actually get all of this code
and go here into the helpers.
And so here let's create a function
and export it, called getJSON.
And so this function is going to be very similar
to what we did in the async JavaScript section.
So it's gonna be an async function
which will basically do the fetching
and also converting to JSON all in one step.
And so with this,
we abstract all this functionality into one nice function
that we can then use all over our project.
And probably, we will just use this actually in our model
in this case, but it's still good to have this function
in this kind of central place.
Okay.
So anyway, this getJSON function, we'll take a URL,
and so of course, this is then what we fetch here.
Right.
And so with this, we can then reuse this function here
for all kinds of your URLs.
Now this here actually already makes sense.
And this one here as well.
So let's now put all of this into a try block,
and then catch the error here.
Now, what are we going to do with the error here?
Of course we could log it
or alert it as always to the console
but let's say we actually want to handle that error here
in this function.
So right here where we will actually use the getJSON method.
And actually let's do that right away.
So, import that first of course,
so import, and again, we are using named imports
and I simply want to import the exact variable name.
So that's getJSON
from
helpers and it's not appearing here.
And I just noticed
that helpers is actually in the wrong folder.
So I put it in a views folder,
but it's supposed to be in the JS folder.
Okay.
And so let's now actually use getJSON here,
and then think about the error handling
that we were just talking about previously.
So here let's say constant data
will be a white
getJSON and then with this same URL here.
Okay.
And so again, this is exactly what we had done before.
There is just one thing missing here.
Now we get these errors, which is really annoying
but anyway, there's actually still something missing here,
which is to then actually return the data
from this function.
Right?
So we didn't do that yet.
And so of course we want to return the data.
And now just as we did also in the last video,
we have one async function here.
So the load recipe function calling another async function
which is this, getJSON function.
Now this function returns this data variable.
And so therefore this data is going to be the resolved value
of the promise that the getJSON function returns.
And if that's still sounds super confusing,
then you might just revisit that lecture
in the asynchronous JavaScript section.
But, anyway what you need to keep in mind is that here
this data will become the resolved value of this promise.
And so here, that's why we then await that promise
and store that resolved value into the data variable.
And so then the rest here is going to work in the same way.
But now back to error handling,
and let's say here we want to actually not alert,
but to log that error to the console,
let's say console.error.
And then here the error, and let's say, we want to do it
in a special way, just so we see which errors are our own.
So using some emoji here should make it really visible.
So this this the CLO temporary error handling
which we will improve dramatically
in one of the next lectures.
But anyway, the point that I'm trying to make
is that the error is actually occurring here, right?
It is going to occur in the getJSON function and not here.
Remember that.
So, when there's going to be an error here in this function,
then the promise that the getJSON function returns
is actually still being fulfilled.
So it's still basically a successful promise
even if there happened an error here.
And so let me show that to you actually.
So, now everything works just fine
but let's now use some invalid URL here.
Then we get some error here
but that's not the one we are interested in.
So the actual error is this one here and it's coming now
from helpers.js at line five,
or actually line 11.
So this is the error that we're logging right here.
So this one here, right?
So we're logging it to the console here,
right in the helpers function.
However, this is actually not
where we want to handle that error.
We really would like to handle it
right here in model.js.
So basically instead of this error message here,
which is simply a consequence of this first error,
we would like to get this error message inside of the model.
Right?
So do you remember how we do that?
Well, we have to re-throw the error.
So we have to again use throw,
and then simply take the error object
that we already have and simply throw this new error.
And so now with this, the promise that's being returned
from getJSON will actually reject.
Okay.
And so then we will be able
to actually handle the error right here.
So let's try that again.
And indeed now we get that same error message
that we had before right here in the model as we want it.
Okay.
So we basically propagated the error down
from one async function to the other
by re-throwing the error here in this catch block.
And that's really important to do.
And we will actually come back to this
in one of the future lectures.
Okay.
And now to finish,
let's make this function here a little bit more robust
and more real world by adding some time out.
So basically setting a time
after which we make the request fail.
And so this is important in order to prevent
for really bad internet connections
where then this fetch here could be running forever.
And we actually already did that previously
in the async JavaScript section.
And so here in the controller, stagger code,
I had already this timeout function.
So we don't have to code it all over again.
And so let's put that here.
And so basically what this function does,
is that it will return a new promise.
So this promise here,
which will reject after a certain number of seconds.
And so in order to now use this function here,
we will have a race between this time out promise
which will take whatever seconds we pass into it,
and this fetch function here,
which is the one responsible for getting the data.
And then whatever occurs first will win the race.
So let me just put that in code actually.
So, remember that works by using Promise.race,
and this takes in two promises.
So the first promise is this one,
and then the second promise will be time out
with a certain number of seconds.
And here for now, let's just say 10 seconds,
or actually let's just say,
0.5 seconds
so that we can actually test this.
So here we're still getting some errors from somewhere
in our code because of this error,
but nevermind.
Oh, and now Google Chrome crashed and here we are back.
So let's just again, click somewhere here.
Now we still got this weird error.
So let's actually check out what's happening here
with cannot read property json of undefined.
While maybe it's already because of this timeout here.
So let's try to put it to like five seconds.
Let's try it again,
but it's gotta be something else.
So it seems like there's some problem somewhere here.
So here, for some reason it's this parenthesis
which doesn't make any sense,
but it is via code,
which for some reason puts it there for us.
But this parenthesis doesn't make any sense
because now this looks like just one element of this array.
So lets just create this promise outside.
So fetch promise is going to be this.
And so hopefully that will fix it.
Well, it doesn't.
Oh, but I see what's the problem.
I'm actually not even calling promise.race.
I only put the records there.
So that was probably the reason.
And so now it should work indeed.
And So if we reload this, then yeah, it works.
So now we can actually test
what's happening here with the time.
So sending it to over a second.
And so that still works.
And so that's make our network a little bit slower.
So I slow 3G and this should then take some time.
And so let's see if we can now time out the request.
So let's wait for it.
And indeed,
now the request took too long time out after half a second.
Okay.
So let's analyze what happened here.
So we passed in half a second here.
And so after that time has passed,
this promise simply rejected with this error message.
And so as soon as any of these promises here
in the race rejects or fulfills,
then that promise will become the winner.
And so therefore we now have a rejected promise here,
which will then trigger the catch block here.
And so that error will then be thrown again here.
And then it will make its way here
into the load recipe function
and eventually will be handled down here.
And so that's why we get these four
explosions down here in the error message.
All right.
Now of course,
then in the real world, we will want to do something
with that error, but again,
a bit more on actual error handling
a bit later in the section.
For now, let's set a more realistic value here.
So of course we don't want to timeout
after just half a second.
Let's say like 10 seconds.
Now, in fact, this is one of these
so called magic numbers or magic values,
which is like a value that seems to appear out of nowhere.
So if someone reads this code, they will find this number 10
and not really understand what it's doing.
And so one more time,
this is a perfect candidate for a configuration value.
So this is really something
that we might want to change about our application.
And so then, if we want to do that,
all we have to do is to come to our config file
and change that here.
So lets call this one time out seconds
and set it to 10.
Okay.
Then here we can import that.
So again is a named import.
And so here we use the curly braces.
So a timeout seconds from
helpers.js.
And this import that is already here
was probably put here by a parcel.
So I don't remember doing this here.
And so it must have been parcel who put us here
in order to make the code work.
Okay.
And so now here we can use the time out second constant.
And so now, whoever is going to read this code
will see that this is one of the configuration values.
So, first because it is in this upper case.
And so that helps already with understanding the code.
And then also because now it is a variable.
And so it's no longer like a magic number
that we don't really understand where it's coming from.
Okay.
And so that's exactly
what this configuration file here is all about.
So, these are two important files
that are used in many applications in the real world.
And so make sure to also include them
in some way in your own applications, in the future.
Okay.
And now in the next video,
we will kind of finish this architecture
and to really implement a missing part here
which is kind of handling the events in a better way.
And so let's go do that right now.