Follow me on twitter here.
Subscribe to the sailscasts mailing list here.
The repo for this episode can be found here.
In the last episode we implemented a request to
/signup using a Sails blueprint action. The default blueprint create action is fine but I really don’t want to save passwords in clear text and I also want to generate a gravatar for each user. Let’s overwrite the blueprint create action with our own signup action. This will also let me introduce a new concept to the Sails community — node machines.
So let’s head back to the editor and open up the UserController.js file. I’m going to create an empty signup action here:
1 2 3 4 5
Now, let’s navigate your browser to node-machine.org which is kind of the mothership of node-machine information. The first thing to understand is that machines are built on the existing and very powerful node module framework. Second, the node-machine specification and the machines located at nodemachine.org are completely open source and MIT licensed. Third, machines are packaged up in something called a machinepack that can be required just like any other node module from npm. So, I’ll show you how to create node-machines in future screencasts, for now, we’ll be consumers of a couple of them.
I know I want to encrypt passwords so let’s browse the existing machinepacks. So here’s one called passwords. And there’s a machine called encryptPassword that looks kind of interesting. With machines you know exactly what inputs you can use and exactly what you’ll get out from the exits. I’m just going to copy the example usage here and paste it into my signup action. I’ll also move my existing response to the success exit of the machine.
I’m also going to move our response into the success exit. Finally, I’ll grab the password as an input to the machine from a request parameter and if there’s an error I’ll let Sails handle it with res,negotiate and respond with the error. I’m also going to change the default result arguent to a more meaningful name like encrypted password.
So in addition to encrypting the password I want to create a gravatar url from the email address that we get from our user. Let’s go back to node-machine.org and browse to the gravatar machinepack. The getImageUrl machine returns a URL of a gravatar based upon an inputted email address. This machine actually has two ways we can set it up. The example usage is using `execSync()
, which allows me to use the machine sychronously. I want to use the machine asynchronously so I'll use.exec()“. Therefore, I’ll also provide both success and error handlers.
So we’ve encrypted our passwords and generated a gravatar URL. Next we’ll save these attributes along with the other fields in our signup form using the create method of our user model. I’ve also added an attribute lastLoggedIn and assigned it the value of the date method. If we get an error I’ll let Sails handle it with res,negotiate and respond with the error. Otherwise I’ll return a response with the id of the new user record as json.
We need to install the two machines we’re using. Open up the terminal window and type npm install machinepack-passwords -save. Next, we’ll install the gravatar machinepack by typing
npm install machinepack-gravatar -save.
Okay, let’s make sure all of this works. Restart Sails using sails lift. Again we’re going to get this message about migration. Our choices are: Safe, Drop, and Alter
If I set migrations to
safe, sails doesn’t do anything other than create a connection to the database and run queries. It’s the default environment for production and should be used whenever you are working with data you don’t want to risk losing.
Instead of trying to migrate the data in the database this mode drops the database and creates brand new tables, essentially giving you a fresh start. If you have a bootstrap file that resets your data on every lift then the drop migration is a good way to get going when your models are constantly changing in the beginning of a project.
alter is next step up from
drop. The difference is that before it drops the database it tries to store all the records in memory and when the table has been re-created it attempts to re-insert them into the new data structure. It’s useful if you have a very small data set and are making trivial changes to the data schema. We’ll ultimately be using safe mode but for now we’ll use alter. We can set this mode by going into the config/models.js file and uncommenting this line.
So let’s lift sails using sails lift. Navigate your browser to
localhost:1337/signup. I’ll add another one of my heros Neil Armstrong. And as you can see we have a new record with an encrypted password, a gravatar URL and our lastLoggedIn Attribute.
So far we’ve been using our user model without any attributes. And that’s fine, especially for nosql databases. In this current project, we’re using Sails local disk database that has characteristics of a no-sql database. We’ll be swapping it out for mongo in an upcoming episode. At this point, I want to prevent duplicate users and I also want to prevent the storage of fields other than those I define in my model. This is just a double check because we’re specifying the fields we want to create in our controller action. So we’re currently in the user model via the /models/User.js file. I’m going to add some attributes here. We’re already doing a bunch of validation client side, but since those are not necessarily secure, we’ll reinforce the validation on the back-end. I’ll use the required attribute on some of the fields and the unique attribute on the email field. This will prevent duplicate email addresses from being saved.
So let’s go back to our signup action in the user controller. First I want to parse the error I get back from Sails regarding the duplicate email address. Let’s log the error and take a look at what we get back. So in POSTMAN with Sails lifted. I’ll create the Nikola Tesla record. Now when I try to add Tesla again, I get the unique error. So let’s go back into the action and handle that error. This also let’s me introduce you to custom responses. So if an error exists and it has to do with the email attribute, and the rule that’s being violated is the unique attribute, then I want to respond with this emailAddressInUse function. I’m going to place that function as a custom response in the responses folder. Let’s go ahead and take a look that resonse. So, I’ll get access to the response and then return a more easily parsable object back to the client. So let’s re-lift Sails and in POSTMAN try to add Mr. Tesla again and we get our error object.
Let’s provide for a better user experience to this error on the front end. First we’ll go into the SignUp Module and inject the toastr service we started in the last episode. This will provide us with some great looking messages. Next, we’ll do something similar in the SignupController injecting the toastr service into our controller. So now we need handle the duplicate error response from Sails passing an error message to the user. In the last episode we already set this up in the markup of our signup page. So start Sails and navigate your browser to
localhost:1337/signup. I’ll attempt to create Nikola Tesla one more time. And there’s our duplicate user error message, however, the loading state hasn’t changed as evidenced by the animated Create Account button. Okay the reason for this is something most refer to as old age. But the good news it’s an easy fix. So, head back to SignupController and change signupForm.location: false to signupForm.loading: false which is what actually was intended but for you know what. Just to show you it’s fixed I’ll add Nikola again for the last time. And we get both the message and the button with the correct loading state.
Alright, in the next episode we’ll add our login state through a request to /login, our profile page, and a database switch to mongodb. Thanks as always for watching and be sure to follow me on twitter and signup for the Sailscasts mailing list. Also go checkout and signup for the beta of the Sails team’s latest creation Treeline.io.