So we had a closer look at cookies.
Now I want to have a closer look at sessions
and really show you that we can use sessions
for other things than authentication as well.
And for this I got a good example here on this page.
Here we got this signup page.
Now let's say we have an error here.
When signing up here,
I do enter two different email addresses here.
Or I enter a password that's too short.
This will then prevent the signup from succeeding
because in our server-side code,
in the signup route,
we do have some validation here.
And if the password is too short
or if our two email addresses do not match,
then we will be redirected back to the signup page
and the signup process will fail.
Therefore, for example here, if I try to create a user,
I end up back on this signup page
and this user was not created.
As you can check by quickly having a look
at all your users,
we still only have one there.
So that's good.
We have this extra validation step,
but the disadvantage of the current approach
is that we're not providing a good user experience.
We all know those websites
where you enter your credentials
and you make one tiny error,
and you are redirected back,
and all your input is cleared.
You have to re-enter everything.
I don't know about you
but I'm not too happy when that happens.
And it's happening here on this demo website.
Of course it's just a demo website
but we can still learn how to do it better.
And here we can use sessions.
Because of what do we want to achieve here?
Well, we want to show this signup page again,
but we want to keep all the values the user entered
so that he or she can simply fix those values
instead of having to retype everything.
The problem we face here though
is that we're redirecting.
I am not returning
a rendered template like this.
I'm not doing this, instead I'm redirecting.
And I am not doing this
because if I would do that,
if I would render a new template
as a response instead of redirecting,
what would happen is that now,
if I reload here,
and I again mess this up real quick here,
then we still end up on this page.
But if I now ever try to reload this page,
I get this, "Do you want to send
"the post request warning again?"
Because this page here
after messing up the data
actually was rendered upon a post request.
And we typically want to avoid sending back
HTML content as a response for post requests
because if a user then ever reloads the page,
he or she gets this ugly warning.
So that's why we want to prevent this.
There also are some other reasons for doing it,
but that's a big one.
Therefore, way early in the course already,
I explained to you that for post requests,
you typically don't want to render
a template as a response,
instead you want to redirect to a get route instead.
And redirect by default will send a get request
to this path.
And then we also load that same page again,
but now through a get request
and therefore, if the user ever reloads it
or anything like that,
he or she won't see this alert prompt.
And therefore that's my preferred approach here.
The disadvantage with redirecting here, though,
is that if we do end up in this situation here,
if I open my developer tools
and go to the network tab,
if I create user,
we actually have two HTTP requests
that are involved in loading this page.
The first one is a redirect request,
which has a 302 status code,
which is the standard status code
used for redirecting.
And with this response, this 302 response,
the final page, to which we want to redirect,
isn't really loaded yet.
Instead this just tells the browser,
hey, please go to this page
with a get request, in this case.
Therefore the browser sends another request,
the get request to this page,
and this request actually leads
to this page showing up.
So we get two requests involved
when we redirect.
The first request and response
that tells the browser that the browser should redirect.
And then the real request and response
that gives us the redirected-to page.
Now, because all of that is the case,
we can't use the data the user entered to us
and just pass it along to the template.
We could do that if we directly render the template,
but we don't want to do that.
And redirect doesn't allow us
to send any data along to the page
or to the route that will eventually be reached.
So therefore here, we need a way
of storing the input provided by the user
somewhere on the server temporarily
until that redirection flow finished,
so until we are on that final signup page.
And if we need to store data on the server
temporarily, sessions are your friend.
We don't just use them for authentication.
Instead this use case here
that you want to store some input data
until the input page is presented again
is a classic example for sessions as well.
So here what we want to do,
before we redirect,
is we want to store that data in our session.
So we can reach out to our session here,
and add a inputData field to it, for example.
The field name is up to you
because you can add any data
you want to your session.
And then here, I will, for example,
add a hasError flag and set this to true
because the input data had an error.
That's why we're redirecting here.
We can add a message if we want to,
where we, for example, say "Invalid input.
"Please check your data."
And we could use this message for showing it to the user.
I'll show you how to do that in a couple of seconds.
And then most importantly,
I'll store the enteredEmail in an email field.
I'll store the confirm email,
the enteredConfirmEmail in a confirmEmail field.
And I'll store the enteredPassword
in a password field.
And now we still redirect without anything else,
but we stored this data here in our session.
Please note that this session data
has nothing to do with authentication.
So setting this session data
will not grant access to the admin page
because there we're looking
for a totally different piece of data in the session.
And that's exactly what I'm trying to convey here.
Sessions can be used for all kinds of data,
not just for authentication purposes.
Now, just as before, I am then redirecting
to a page that will rely on this data
because I'm saving that data
to show it on the redirected page.
And therefore here I will actually, again,
call save manually
so that we can provide a function
that will be executed
once saving to the database finished.
And I only want to redirect
after that session was saved
so that we can be sure
that only redirected page
will have access to that session data.
Because that is now the second step.
We're storing that data in the session.
Now on the get route here for signup,
I want to check if I do have such stored data.
Of course, we'll not always have stored data.
If we visit the signup page for the first time,
for example, we didn't try to submit it yet,
and therefore we won't have the inputData field
in the session.
But if we messed up the signup,
we will have it.
So here we can add a variable
with my session input data,
or however you want to name it,
where I access request.session.inputData.
And of course, that here should be the same key
as was chosen down there for storing the data.
Now this might be null or undefined
if no data was stored in the session before,
and therefore I want to check
if I maybe don't have session input data,
if this is null or undefined
or false in any other way.
In that case, I want to set it to a default value,
which actually will be,
which actually will be an object
where I set hasError to false
because we haven't stored any input data,
so we clearly didn't have an error.
And where I set email to an empty string,
confirmEmail to an empty string,
and password to an empty string
because we don't have any data stored in a session yet,
so we clearly are visiting this page
for the first time.
We didn't submit it before.
So I will set all these values
to empty initial states.
And I'm doing this so that now either way,
if we got it in the session or not,
I can send this session input data
into my template.
Here I'll add our inputData key
and set this equal to a sessionInputData,
which is now either that default object
or the real object with all the data
that was stored in the session.
Now with that,
we can go to the template
and utilize this inputData field.
So we can go to the signup template
and use this data there.
Here, for example, we can first of all
show the error message if we have an error,
maybe below the h1 tag.
We can use EJS to check
if inputData.hasError is true,
which it only is if we're having an error.
Otherwise it will have this false value,
which we set a couple of seconds ago.
If we do have an error, though,
we can show a paragraph here
with an ID of input error
for which I provided some styling.
And in there we can then output inputData.message.
So that's this message key,
which I'm also sending
if we do have an error.
This message here if we have an error.
So that's what I output here,
the message if we have an error,
and then I also want to pre-populate all these inputs
with the default values
that are set if we didn't have
any input data in the session.
So this default data here.
Or with the actual user data that was entered before,
which I did store in the session
in case of an invalid input.
So I want to pre-populate those inputs with those values,
and we can do that with the value attributes.
Here we can use EJS to set a default value,
and that's inputData.email for the email,
and so on.
So down here, it's inputData.confirm,
whoops,
confirmEmail.
And here for the password,
it's inputData.password.
Like that.
Now with that, if we visit
the singup page for the first time,
if I go to a different page and come back,
it's empty because now we have
no input data in the session,
and hence the default values,
which are those empty strings, are set here.
We also don't see any error message.
But if I do now create a new user
and I do enter some invalid credentials here.
If I click Create user,
now we are redirected,
but now our entered data is preserved
and we see this error message.
Though, you will notice that if you now try
to visit any other page,
this will fail
because our server actually crashed.
We'll fix this in a second.
But before we fix this,
it is worth noting that we were able
to preserve the entered data
and show this error message.
But now why does our server crash here?
The reason for that can be found in the post signup page.
Here, I am redirecting after I saved my session.
The problem is that if I return here,
then I'm returning in this anonymous function.
So returning in here will not prevent
the other code from executing here.
Instead, it only prevents other code
inside of this anonymous function from executing.
And there is no other code here.
Therefore actually, I did redirect,
but then all that code here still executed,
and therefore actually, I did even create
this user in the database.
And then I redirected again.
I sent back two responses
and that's not something Node Express likes.
That's why you get an error like "Cannot set headers
"after they are sent,"
because we tried to send another response
and therefore sent new response headers
after already sending one.
The fix is easy.
We just also have to return
after saving our session
so that we still ensure
that in this main function here,
this code down there doesn't execute.
On the other hand, we don't need to return
in this anonymous function
because there is no code in there,
which we would want to prevent from executing.
So we should return after saving the session.
That's important here.
And now, of course, I also briefly want
to have a look at my users and delete that user,
which I accidentally created,
simply by running db users deleteOne,
and then using the email for that user
that I used before.
So that I clear this erroneous user.
With all of that, if you now stop that server
and restart it,
you should be able to visit the pages again.
And now if you submit this invalid data again,
the server didn't crash,
and now this user also wasn't created here.
So now that's this bug fixed.
And now we preserve that data
so that the user is now able to correct it
and then submit valid data.
However, you'll notice a problem.
I do now do that and this worked
because I entered valid data,
and hence I could now log in
with that newly created user.
But if I visit the signup page,
the data is still there.
It's there even after I reload and so on.
And it's still there because it's stored in the session.
And thankfully, data doesn't randomly disappear
in the session.
That would be pretty bad for our authentication use case.
Instead, data exists in a session
until the session expires
or you manually delete it.
Like we're doing it in the log out route.
There we are deleting the authentication data
from the session when the user logs out.
Now for this validation use case here,
where I'm storing this entered data
to provide a better user experience,
I actually want to clear the data
from the session
right after presenting that data
to the user on the signup page again.
So here, in this get signup route,
right after extracting the data
from the session and using it,
I want to delete it.
So here, before I even rendered a template,
I'll go to request session
and set the inputData for that user
to null again,
to effectively clear it.
We extracted it before,
so it's still stored in this variable,
and we can still send it to the template.
That will still work.
But we also clear it from the session thereafter.
And as mentioned before,
the session will be saved
to the database automatically.
And here we don't need to ensure
that saving was done because for this page,
for this template, which we render,
we now don't directly rely on the session anymore.
But by resetting it here,
we ensure that the data is only kept around
temporarily in the session
until the redirect finished
and until we extracted the data
from the session once.
And that's absolutely something
we should do here for this use case.
So you can use sessions for different use cases,
and it depends on the use case
how long you want to store your data.
Here, I only want to store it for one request.
This technique is also called flashing,
where flashing a value onto the session
just to have it for the next request
after a redirect,
and thereafter it's cleared again.
With this now, if I reload this page again,
and then again,
now the data is gone.
So now if I try to create a user here
and I mess up the data,
I still get this better user experience
of showing the error and so on.
But if I come back to the page in the future,
it's not there anymore
because we clear it after presenting the page once.
And that's how we can also use sessions here
for validation purposes.