Now with that connectToDatabase,
we are finally ready to work on storing users.
And for this, since we'll use the MVC pattern,
I'll go to my models folder and add a user.js file in there.
Or we could also name it user.model.js
to be inline with the naming convention I used
on other files where I also described those key files
and what their role is,
but you don't need to add a .model.js,
this is totally optional.
Now here in the user.model.js file,
I'll start by creating a class User.
You'll learn that classes are a JavaScript feature
that allow us to define blueprints
for objects that we wanna create in the future.
And I do wanna create user objects in the future
with the intention of then saving them in the database
or logging them in.
Now in such clauses, you can add constructor methods,
one constructor method per class to be precise,
which is a method that will be called automatically
whenever you create an instance based on that class
with the new keyword.
Then the values you pass between those parentheses here
will be the values received in the constructor function.
And we'll create instances based on the user class
with the new keyword later.
Here we can then use the constructor
to receive some values that we need to initialize,
the to-be-created object.
And for example, here, I wanna get the email
and the password of the to-be-created user.
I wanna get the full name of the user.
I also wanna get the street, the postal code and the city.
These are the different values I wanna get
for every user that's created.
Or to be precise, these values should actually be optional,
but I always wanna have email and password.
And by adding these optional values
at the end of this parameter list here, I make them optional
because in JavaScript, you can call a function
with free parameter values, for example,
with just two values, and then the omitted parameters
will simply receive a default value of undefined.
So you can call a function or method
with less parameter values than it expects.
So that makes those parameters here optional,
because we can simply call the constructor
with just two values instead of those values.
And that gives us the flexibility of creating users
with more or less data.
Now, here, I then wanna store
all the received values like this.
And by using the this keyword here,
we are referring to the to-be-created object.
We add a property, a key named email to it,
and we set the value of that property
to that email we're getting here in the constructor.
And we don't need to have the same names here
on the left and right side of the equal sign.
I just refer to this email here with the email here,
so I'm using the constructor value I'm getting.
This name here is totally up to you, on the other hand.
This could be a totally different name as well,
it doesn't have to be the same name.
But of course I am storing an email address,
so it makes sense to pick email as a name here as well.
And I'll do the same for the password
and store this in a password property
of the to-be-created object.
And I do the same with the name.
Here I store full name in a name property,
and here we also see that we don't need
to have equal property names.
I will also add an address property,
and that should actually be a nested object,
which you can also create
with this object literal notation in JavaScript
with opening and closing curly braces.
That's how you can create an object in JavaScript
an object on the fly,
which is not based on a predefined class.
So that's an alternative to new something,
whatever your class name is,
you can use curly braces like this
to create an object on the fly,
which is not based on any blueprint.
And here, I just create this object on the fly
to group all the address data together.
So I'll add a street field here,
which stores the street value.
And here, this property name is up to you.
This property name here on the apprehend
refers to this parameter value here.
Parameters in the end are like variables
inside of the function where you use them.
So here we need to have equal names.
This name on the apprehend doesn't have to be equal
as I just mentioned.
And then I also store to postal code.
Here you see that the names don't have to be equal.
And the city.
So with that, I'm storing all the user data,
but of course I'm not storing it in the database with that.
I'm just storing it into to-be-created user object,
which is created based on this user class.
But this will be a first step.
Because with that added, we can add a function,
a method to this user class,
which could be called signup.
And the idea of this method is
that it does actually store that user data in the database.
So that it takes the properties of the object
on which this method is called
and stores the values stored in those properties
in the database.
Now for this, we wanna use our database helper file,
we worked on in the last lectures.
The file where we get access to the connected database.
For this, I will import it with require.
And since I'm in the models folder,
and I want this database.js file in the data folder,
I have to go up one level first with ./
so that I'm now in the main project folder.
And then go into the data folder
and then access the database file in there.
And this gives me access to the exported object here
in this database.js file.
So that I can use it into user.model.js file now.
This db thing here can now be used in the signup method here
and there, I now wanna call getDb
to get hold of that connected database.
And then access a specific collection in there.
Because remember when working with MongoDB,
you have collections in a database.
Collections of documents.
Collections are basically like tables in a MySQL
or in SQL database in general,
but they have no fixed schema.
They have no fixed data types. They are more flexible.
Now you pass the name of the collection
to this collection method.
And the name is up to you, just as with the database,
it will be created on the fly if it doesn't exist yet.
And I'll choose users here,
which sounds like a fitting collection name
for storing user data.
And now to insert a new user,
which is what I wanna do when a user signs up,
we can call insertOne.
That's how we can insert one new document.
Then a document here in JavaScript
with the MongoDB package is just a JavaScript object.
So we can pass an object to insertOne.
And here I am, again, creating one on the fly
and that will then be stored as a document
in the MongoDB database.
Now it's up to you, which data you wanna store.
But in the end here, I wanna store the email,
which is this.email.
I wanna store the password, which is this.password, maybe?
We'll come back to that.
I wanna store the name, which is this.name.
And I wanna store the address, which is this.address.
So I basically wanna store all the data
that I collected and set in my constructor,
but there is one exception or one special thing about that.
And that's related to the password.
Whenever you're storing passwords in a database,
you don't wanna store them as plain text.
That's what you learned in this course.
And that is super important.
It's a security issue to store passwords as plain text,
because if your database ever gets compromised
by an intruder or an unhappy employee,
the attacker will be able to read
all your user passwords in plain text.
And since users tend to use the same password
for different services
and an attacker would now have to email
and password combinations of your users,
he or she could use these email/password combinations
to access user data on totally different sites,
at least to try to get access.
And therefore you don't wanna store passwords in plain text.
Instead, we wanna use a technique that is called hashing,
which means the password is converted
into a randomly looking string, which actually isn't random,
but which can't be decrypted back
to the original password.
Instead, we have that hash
and we can just verify whether a future password
matches that hash,
but we can't decrypt to hash back to a password.
And that's a good protection mechanism for passwords.
Now, creating those hashes manually is cumbersome,
that's why I will install a third-party package again.
And that's the bcryptjs package here.
That is a package that will help us with encrypting
or with hashing passwords.
And once it is installed,
we can restart the development server.
We can import this package here
in this user model file by requiring bcryptjs.
And actually it is considered a good convention
to have a built in or third-party package imports first,
and then your own imports thereafter.
It's not technically required, but a good practice.
And with that imported here in signup,
instead of just storing the password like this,
I now just call bcrypt.hash, and then pass my password,
this.password as a first parameter value.
And then the number of salting rounds, as it's called,
which basically just controls how strong
the hashing algorithm is as a second parameter value.
And I'll use 12 here, which is considered
a sufficiently strong number of salting rounds.
So that should give us a strong hash,
which can't be a decrypted back to the original password.
Now hash actually can take a bit longer,
hence it's an async operation
just as insertOne is by the way,
this is also an async operation.
And therefore these operations return promises,
which we could handle with then and catch,
or we turn sign up into an async function.
Therefore to sign up method itself
will all the return a promise,
and we can then await this hashing here
and just store the final value in a constant
or variable as we always do.
In this case, the hashed password like this.
And then we also await inserting this data.
And here, instead of this.password,
we now use the hashed password.
Now insertOne will return a result,
which we could store and use,
but here I'm not too interested in it.
Therefore I will actually not store it.
And instead, just await this and after this, we'll be done.
So this method execution will only finish
once this insertOne step finishes.
And that's what I want here.
And this should allow us to store users in a database.
Therefore with all that work done in this model,
in this user model here,
we can now make that model available outset of this file
with module exports.
And here I'll then just export this user class as a whole.
So I don't export an object with multiple combined things,
I just export the user class here like this
in this user.model.js file.
And then you auth controller, we can now use that.
Here we can import user by requiring,
going up one level models, user.model.
So we are requiring that model we just export it.
And now in this signup function,
which will be triggered once to sign up form is submitted.
We now can use this user model to create a new user
based on this blueprint with the new keyword.
That's how you use classes in JavaScript
to use the class blueprint,
to create a concrete instance of that blueprint.
So to create a new object based on that blueprint,
and we could then store the newly created object
in a constant name user.
Now to user here, we can pass various values
because our constructor function accepts various values.
And here I wanna pass in all those values,
but for that, we need to be able to extract
those values from the incoming request.
And that can be done by express with the proper middleware.
We haven't added that middleware yet.
And therefore we have to do this in app.js.
In app.js because I wanna try to extract
any attached request data
for all incoming requests.
And therefore here, maybe after handling static of files,
we can use app.use,
and then use the express.urlencoded middleware.
This is a function you have to call
because that function then actually yields
the actual middleware.
And this is a function because we can pass an object
with configuration to it.
And there, in this object, you can set extended,
and it is quite common to set this to false,
to only support a regular form submission so to say.
So now with that, we are handling data, that's coming in
attached to requests, and specifically we're handling data
that's coming in because of forms being submitted.
Now we can go back to our controller,
to the auth controller.
And in there, on this req object,
we now have access to req.body.
And then with the dot notation,
we can access all the fields we defined in our form.
So if we have a number, look at our signup.ejs file,
where we have this form, then there it's these name values
that we can now access as keys on the request body.
So email, confirm email, password, full name,
street, postal, and city.
So therefore going back to the auth controller,
here for the email, we can access request.body.email,
for the password we can use req.body.password.
Then for the full name, it's req.body.fullname,
that was the name of the form.
And then for the street, we have req.body.street.
Thereafter for postal is req.body.postal.
And then for a city it's req.body.city.
Now I'll press the auto format shortcuts
to make this more readable,
and that now creates a new user with all that data.
Thanks to our blueprint.
Now on that user, we can now call the signUp method,
which we defined to store that user in that database,
because that's what this signUp method does in the end.
Now this signUp method returns a promise
because it's an async method.
And therefore we wanna await this
before we sent back our response.
So here I will also add async
in front of this signup function here in my controller file
so that we can now await this signup method call here.
And then we know that once we make it into the next line,
we'll be done with creating that user.
And therefore here, I then wanna send back a response
and it will actually redirect here.
For controller functions that are triggered
upon incoming post requests,
it is pretty standard to redirect
instead of rendering a template as a response,
because if you would render a template,
if the user presses the refresh button of the browser,
you get this pop-up wherever you wanna send post data again.
Now with a redirect, you instead redirect
to a totally different route, and you wanna do that
after handling the submitted data successfully.
So that the user can't accidentally resubmit
the post route data.
Therefore here, I wanna redirect,
and I wanna redirect to /login here.
We don't support this yet.
I'll add some logic for this in a second,
but this will send the user to the login page
after a user was created.
And that makes sense, because once a user was created
as a next step, we wanna allow the user
to now log in with that created user.
So now, to be able to test this,
I will quickly go back to signup.ejs,
copy the entire markup in there,
and paste that into login.ejs.
And in there, I just want to
change the h1 text to Login.
And then the form I'll get rid of the confirm email
label input combination.
And then have the horizontal line
and all the inputs thereafter
because for logging in, we don't need any of that.
We just need email and password for that.
On the button, I then also wanna say log in.
And on this link down there, I wanna redirect to /signup,
and say Create a new user,
like that.
So that's now my login.ejs file.
And I quickly created that, so that in the auth controller,
in getLogin, we can now res.render this template
so that redirecting to /login will succeed later.
And I wanna res.render customer/auth/signup,
or at least I'd copied that.
But then I wanna render a customer/auth/login
to render this login.ejs view.
Now with that, we should be able to sign up.
This is not the finished code, there is more we can do,
but we should be able to create new users.
And we'll then refine this process thereafter.
But first of all, let's test whether that works.
Let's go back and reload this form,
and let's enter some details here, a email address.
And for the moment,
you can have different email addresses here,
because we don't have any validation on this server
at the moment.
So then a password then a name here,
and I'll pick my name and then some street,
some postal code, which has to be five characters long,
thanks to the front end validation we added,
so thanks to these HTML attributes we added.
And then a city.
If I now create account,
I'm redirected to login.
I also don't have an error here in the console.
So the server didn't crash, so that's looking good.
And if I now connect to my database with the MongoDB shell,
I can use this online shop database to which I'm connecting
and then run db.users.find
to find all the users and the users collection.
And I find one user here.
Now it might be a bit differently formatted for you,
but I find one user here.
And if I scan over that, I can see that all the data
for that user is there and that the password is also hashed.
It's this unreadable string.
And that's good.
With that, we created this user.
We're able to store users.
Now, we still got some improvements to make
about this signup process though.