Follow me on twitter here.
Subscribe to the sailscasts mailing list here.
Howdy.
In the last episode we finished out the signup page. We now want to take that information and allow users to login, better known as user authentication. So far we’ve created one Angular module for the signup page connected to our Sails API. And this is under a broader category of public pages or those pages that don’t require a user to be logged in to see them. We also need a homepage that will also be public. Finally we need a view, I’m going to call the dashboard that will require that the user be logged in to our back-end.
So we have three Angular modules for activityOverlordv2.0. You can of course accomplish the same end result in a variety of different ways, but I’ve chosen this approach for simplicity and ease of SEO.
Ok, let’s start with the homepage.
We’ll first replace the current mark-up in homepage.ejs within the view folder with this new mark-up. The markup contains our top level navigation, which includes the sign in button that triggers a PUT
request to /login
, as well as a sign-up button that triggers a GET
request to /signup
. I realize I’m going through this pretty fast but don’t despair, I have a repo that has all of the code for your review.
Let’s look at the Angular module and controller for the homepage. The homepage and signup module’s are identical both inject the toastr and compareTo services. The homepage controller adds a new method to the scope called submitLoginForm
. Similar to signup form, submitLoginForm
initially sets the loading state to true
. We then make a PUT
request to /login
passing in an email
field as well as a password
field from the $scope object. If the request is successful, we’ll make the equivalent of the a GET
request to /
. If we’re unsuccessful we’ll handle the known errors, in this case a bad email/password combination through the toastr
service and return. We’ll also handle any unexpected error here. Finally, in either case we’re toggle the loginForm
loading state back to false.
So now that we have the front-end set up for the Homepage
, let’s switch gears to the back-end Sails API.
The first thing I want to do is handle the PUT
request to /login
. So I’ll add that route
to the routes.js
file here. We want this to trigger a login
action in our UserControlller
. Next, we’ll head over to our user controller in UserController.js
. So let’s add the add the login
action. The next block of code I’ll add first takes the email address that was passed from the login form and searches our user
model for an existing user. If it doesn’t find one we’ll send back a 404
status which will trigger the toastr
message in the HomePageController. Next, we’ll be using another machinepack, called machinepack-passwords. Within the machinepack we’ll use the checkPassword
machine passing in the user provided password from the login form as well as the encrypted password found by the findOne
method of our user
model. Within this machine
we handle any unexpected error first and then if the passwords
don’t match we again send a 404 status
via res.notFound()
. For reference, these responses can be found in the responses folder here. If we get a match, we need to log the user in so we’ll create a new parameter on the session via req.session.me
and give it the value of the id
of the user we found earlier with findOne
method of the user
model. The last thing we need to do is let the front-end know everything went well and we do that by returning with res.ok()
.
Let’s see all of this in action. I’ll start Sails with sails lift
and we’ve got an issue here. I know what it is because I’m old and forgetful. So, I’m going to go into the routes.js
file and you’ll see that I just missed a comma here. So let’s go back and try that again. Okay, Sails is up and running, let’s go to the browser and let’s hit localhost:1337
. Okay, so the reason we’re getting a 404
not found is we don’t have a route that handles a GET
request to /
. Now we could remedy this very quickly by going back to the routes.js
file, and we’ll create a route here, that handles a GET
request to /
, which will trigger the homepage and we’ll restart Sails here…go back into the browser and refresh and ther’s our homepage and that’s great. But what I really want is a way to marshall between whether a user is authenticated, that is, logged in, and if they’re logged in I want to display one front end and if they’re not logged in a different front end. I can do that by adding a page controller to Sails. So let’s do that. First, I’ll add the controller via sails generate controller page
. Now let’s go back into Atom and we have our PageController
here which is empty. I’m going to add an action here showHomePage
.
We first check if the user is logged in via the me
parameter on the session, so, if req.session.me
does not exist, I want to load the homepage and I’m going to do that by return res.view('homepage')
. Otherwise if the user is logged in, I’m going to use the findOne
method of the user
model to look for that user. So I’ll pass in the userId
which is the value of the me
parameter on the session. First I’ll look for an unexpected error here and I’ll handle that with res.negotiate
if it exists. If I don’t find a user, I’m going to return the user to the homepage. Now if I do find a user, I’m going to return a view that we haven’t created yet called dashboard
. I’m going to bootstrap some information that I found via the findOne
method of the user model on the page itself.
Before we create the dashboard view you may be asking what’s going to trigger this showHomePage
action. Well, we’re going to do that by going into, routes.js
and substituting this current view with our PageController
and our PageController
action showHomePage
. So now any time there’s a GET
request to /
our page controller will marshall which front end the user receives.
Now let’s implement our dashboard page. The mark-up for the dashboard page is straightforward. As with the signup and homepage views, the dashboard view will have some top level navigation. The part I want to concentrate on is the bootstrapped data we get from the page controller’s showHomePage
action. So using ejs
we’ll add the parameters of the me
object from the showHOmePage
action on to window.SAILS_LOCALS
.
Next, let’s add the very minimal dashboard module and controller. I’ve added these files under a separate folder named private
to distinguish between the Angular code that will be executed when a user is authenticated and code that is public and does not require authentication.
I want to ensure that the new Angular modules I’ve added will be loaded before any controllers, so I’ll go back to pipeline.js
found in the tasks
folder and add the path to the two new modules — HomepageModule
and `DashboardModule’. This will insure that the modules will be loaded into the page first.
Okay, let’s see this all in action. So I’ll start Sails, with sails lift
, and then navigate my browser to localhost:1337
. In the previous episode we created an account, nikola@tesla.com
with the password 123456
and I’m going to use that now. Now if you don’t have that user created, no big deal, just click on the sign up now button and create that user, nikola@tesla.com
with the password 123456
, then go back to localhost:1337
and start from there…and we have our dashboard page. And if we view the page source, we can see that we have our bootstrap user right here on the page. So let’s go back to the PageController
really quickly here…req.session.me
existed, we used that with the findOne
method of the user
model…it found the user and then passed the user object via the me
parameter and bootstrapped it onto the dashboard page.
So there are a couple of things we still need to implement here. We have this sign-out link, that if i click on it it’s going to give us a 404
and that’s because we don’t have a route that handles that link…so this link is making a GET
request to /logout
and we don’t have a route for that so let’s take care of that first. So now that GET
request to /logout
will be tied to or will trigger the UserController
and this new action on the controller. So let’s go up to UserController
and let’s add a logout
action.
So similar to our login action we’re going to look for the user id on req.session.me
…we’re going to pass that to the findOne
method of our user
model…we’re going to handle the errors if they exist and if it finds a user, we’re going to essentially log that user out by making req.session.me
equal to null
. Then we’re going to return with a new response, backToHomePage()
. So let’s take a look at that response.
So this response is basically for convenience…if logout were an AJAX request, Sails will know that that request just wants a JSON status code response. In our case we’re making a GET
request via the browser, so we just want to redirect back to /
and let our page controller handle it. Okay let’s see this in action.
So I’ll go and restart Sails, and we’ll go back and log-in. So now when I hit the signout link the PageController
sends me back to the homepage.
There’s just a couple of other things we need to do. Within the UserController
, signup
action we want the user to be logged in after they’re signed up. So we’ll do that right here. And within the signup page controller, on a successful signup we made a GET
request via the browser to /user
and now we just want to ake that /
.
Let’s go ahead and restart one more time. And we’ll now go to signup, and I’ll create a new user b
, b@b.com
…and everything works. So the user was created and logged-in…and let’s take a look at the page source…and there we are there’s our bootstrapped data.
Thanks for making it through the many voices of Sailscasts. Although it might seem that there are different Irl’s on this Sailscast, I assure you that it was the same ole guy throughout…al beit Allergy-challenged. So now we know how to create a user, save it to a database using the Sails ORM, and then use that information to authenticate a user marshalling between different Angular front-ends depending upon the user’s log-in status.
There’s been quite a bit going on in Sails and Treeline so expect an update shortly via the Sailscasts mailing list. If you’re not signed up, head over here to subscribe and you can always follow me on Twitter here.
]]>Follow me on twitter here.
Subscribe to the sailscasts mailing list here.
Howdy.
So, I wanted to do a quick screencast previewing a new project from the Sails crew called Treeline. So, let’s get busy.
In the last two episodes we implemented the /signup
request via Sails blueprints and then a custom controller action. Today, I want to show you how I would do this in Treeline. So I have a Treeline project here, which should look very similar to a Sails project because that’s what it is with some very powerful additions. I’ve copied the assets folder from our existing activityOverlord20 project to the assets folder here. So we have the same front-end asset structure, but I’m going to create the back-end api in treeline.
So here I am in the Main Treeline Dashboard. We’ll go to apps, we’ll create a new app, and not surprisingly we’ll call it activityoverlord20. And the first thing we’re going to do is create our model. Now, in the past episode we created a user mode. We’ll add attributes to that model the first being name and notice that I’m going to add an example, and treeline will infer which type is appropriate. Next, I’ll add the other attributes title, email, password, and gravatar url.
So now that we have our user model let’s define the two routes that our front end requires. The first route will be GET /signup. This takes us into the circuit view. Since we just want to display our signup.ejs view we can specify that in an empty circuit here. Okay, next I’ll create the route to POST /signup. As you may recall we first wanted to take the password from our form and encrypt it. We used node-machines to do that. You can think of treeline as the visual interface for node-machines. So I’ll just drag the encrypt password machine as a part that will fit on something called a circuit. And then I’ll access the password field from our signup page via a route paramter.
Next we want to convert an email address into a gravatar url. I’ll search for the gravatar machinepack and install it. And from with that machinepack I’ll get the getImageUrl machine and put it on our circuit as well. Now similar to what we did with the encrypt password, I need to get our email address from our form and I’ll do that with a route parameter.
At this point we wanted to make sure that we had unique email addresses and if we didn’t to let the front in know via a 409 status code. So I’ll drag a findone user machine over to our circuit and look up the email address provided by our form in the user model. If a record is found, we know the email address is taken and we want to let the front end know so I’ll respond with a 409 status. If a record is not found, we want to go ahead and create the user so I’ll drag a create user machine over and start connecting the user model with our form fields. We’ll do this mainly through route parameters, so for the name we’ll get the route parameter, for the password we’re going to actually get the output of our encrypt password machine. For the email we’ll go ahead and use a route parameter, we’ll do the same for the title field, and finally for the gravatar url we’ll use the output of the gravatar machine.
Okay let’s see all of this in action. So we’ve built the api in Treeline and Treeline syncs the controller and model with our local project when I lift it using Treeline preview. So let’s head to our browser and navigate to localhost:1337/signup which will make the GET request to /signup
and our signup view. Next, let’s go ahead and create a User record – Nikola Tesla.
Let’s go back to the signup page and create Nikola Tesla again and see if our duplicate email message is triggered.
And there it is…so this really just scratches the surface of the power of treeline. In addition to the activityoverlord20 screencasts I plan to do additional videos on this new tool. If you’re interested in using treeline, head over to treeline.io and sign up for the beta. As always thanks for watching.
]]>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.
]]>Follow me on twitter here.
Subscribe to the sailscasts mailing list here.
The repo for this episode can be found here.
Howdy from Austin and welcome back.
It’s now time to get into implementing our Sails back-end api for the signup page. In terms of workflow, we’re going to let the signup page drive the requirements of our Sails back-end. So looking at the signup page I want to find all of the places where the frontend needs to make requests of our backend.
Not to get too nerdy here, but really we’ve already made a request
from our browser to our Sails back-end when we loaded the signup page. When I navigated to localhost:1337/signup
with the browser, the Sails web server passed our request to the router, which parsed the request, in this case, /signup and matched it with a handler in the routes.js file ultimately responding with this view. So now that we have the signup page rendered in our browser, what other elements in the page need to make requests?
There are at least three different elements that will make requests to the Sails back end api. They include /
when the user clicks the activityOverlord2.0 logo, to /login
when the user clicks the Sign In button, and to /signup
when the user clicks the Create Account button. I’m going to implement the request to /signup
first. Now, some of you might be wondering, wait, we’ve already handled a request to /signup
. And that’s true but it’s to GET /signup, where GET is the HTTP verb that the browser tpically uses to request web pages. This new request, however, will use the HTTP verb POST as in POST /signup
. If this is starting to make your head spin, get your balance and head over to this episode which explains HTTP routing in nauseating detail. Actually I, I hope it isn’t nauseating.
Ok, this POST /signup
request will take the gathered contents of this form and send it to the Sails server via an AJAX request from Angular.
So let’s set this up and go b ack in our text editor, wait where’s sublime. I love sublime text but I wanted to experiment with the Atom editor from github for the next serveral episodes….anyway we’re in signup.ejs. And we need to implement this function that the ng-submit directive is pointing to: `submitSignupForm()“. But where do we declare that function? We’ll declare it in our controller which we pointed to via the ng-controller directive here. The actual controller code is contained in a file named SignupController.js by convention because really we could’ve named the fie foo. So here’s where we actually created the controller. So I’ll declare the function here. But this function has no connection or binding to the markup. We’ll use the Angular $scope object to make this connection. Angular characterizes the $scope object as the glue between the controller and directives in the markup. So on the onehand we have controllers contained in javascript files and on the other we have directives contained in mark-up. And the $scope object is used as the bridge between them.
So let’s fire up sails using Sails lift and go into the chrome browser and navigate to localhost:1337/signup
. And as you can see here we’ve got an error…our $scope object has not defined. What’s happened is the $scope has not been injected into our function. But we can easily remedy that by passing the scope as an argument into the function. Now when we refresh the browser, we no longer have an issue. But we’re not out of the woods yet.
Angular determines a controller’s dependencies from the arguments to the controller’s constructor function. So if we minify our JavaScript for the Signup controller, all of the function arguments including $scope would be minified as well. So $scope could turn into the letter a and Angular’s dependency injector would not be able to identify services correctly. But there’s an easy fix here. I can add an array to the constructor here that adds a string for $scope and since strings aren’t minified, Angular has a way to match the name with the argument. Let’s go back into browser and refresh and make sure everything works.
Okay great, Next, we’ll enable the loading states we created in the previous episode. I’ll set-up the initial loading state by adding another attribute to the scope object:
1 2 3 4 |
|
Now, don’t be confused by my adding the signupForm
attribute between the scope object and the loading attribute. I’m just gonna namespace everything concerning the signup form to under this attribute. So when our submitSignupForm() function is executed we want the loading state to change to true. If we look at the markup and specifically the create account button, you can see what’s changing based upon the loading state. If the loading state is false, the button will display the Create Account text. If it’s true, the button will display our spinner animation with the text Preparing your new account within the button. Okay, let’s see if this works. I’ll go back to the signup page and since we have validation I need to put in some valid values and when I click the create account button you can see the loading state change.
So now let’s add the ajax request to our function and we’re going to do that with an Angular service $http. If you’ve ever done an ajax request in jquery, this is going to look very familiar. First, let’s inject it into the function and the array like we did with the $scope object. So we’re going to put in the path and any data we want to send with the request. In order to bind the form fields to these data attributes we’re going to use $scope again. And remember the $scope is linking the values that we’re capturing in the mark-up via the ng-model directive. So let’s do that for the rest of our fields in the form. We’ve got title, and email, and password. Okay great, now let’s declare our success and error handlers. So on success I want to change the location of the browser to /user
. This has the effect of making a GET
request to /user
. If the request generates an error, I want to catch that error and for now just log the response. Finally, I’ll create this catchall function and either way I want to reset the loading state back to false.
We’re now going to transition from the front-end to the back-end. From the angular framework to the Sails framework. Our goal is to create a user by gathering up account information and sending it to Sails. So let’s implement some stuff and then I’ll circle back and explain what we did in more detail.
Head over to the terminal and type sails generate api user. With this simple set of commands and a bunch of tools and automation from Sails, we have almost everything we need for our API. Start the Sails server by typing sails lift.
Because we created a model, Sails is asking us a question about migrations, we’ll come back to this. For now I’m going to use the second option — alter
.
Next, let’s go into the browser and navigate to localhost:1337/user
. We’ve just used one of Sails blueprint shortcut actions. By navigating to ‘/user’ we made a GET
request to the server for all the user records. And since we haven’t created any records yet, not suprisingly we get an empty array. But using the create action blueprint shortcut I’ll create our first user record — Humphrey Bogart. Sails also has blueprint rest or RESTful actions. From within POSTMAN, really one of the best tool on the planet for testing routes, I’ll make a POST request to /user with these route parameters for our next user Aimee Mann. Chances are you’ll create your own Controller Actions by the time you get to production. However, Blueprints are incredibly useful in the design phase of your API. If you interested in learning more about Blueprint capabilities, head over to this episode where I break them down in a bunch of detail.
So the path we want to associate with create a user is /signup
. So head back into the editor and open up \config\routes.js
. I’m going to add the route POST /signup
which will be handled by the UserController.create
action. Let’s restart Sails and navigate to the signup page. Finally, I’ll signup my hero Nikola Tesla. And there’s our list of records.
So briefly I want to go over what we’ve done. We created a route that connects the request to a controller and action. When the api was created, Sails provided us with a user controller and a create action (via blueprints) automatically. Sails also created a user model that has model methods like .create, .find, .update, and .destroy. Finally, the user model can retrieve and save records from a variety of databases via adapters.
In the next episode we’ll transition from Sails blueprints to our custom action. I’ll also introduce a brand new Sails concept — node machines. 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.
]]>Follow me on twitter here.
Subscribe to the sailscasts mailing list here.
The repo for this episode can be found here.
In activityOverlord v1.0 the user interface or Views
of the app were built primarily on the server before being sent to the client and rendered by the client’s browser. This approach is the V
of the MVC architecture and also known as Server Rendering even though the web page is actually being rendered on the client’s browser.
This so called Server Rendering is the server preprocessing one or more templates of markup, usually some combination of HTML, CSS, and Javascript and combining them with data via a Template Engine. All template engines use some form of tags that surround variables that when processed yield a result. Sails uses the EJS template engine by default. For example, this template contains some standard HTML mark-up along with EJS
tags.
1 2 3 4 5 6 |
|
Between the tags is a variable title
. When the Sails server processes this template it will attempt to replace the title
variable with a value, hopefully the page title. This is also called String Interpolation, but I only mention that because I’m a geek.
So let’s review this approach. When a client browser makes a request
to the server, the router
parses the request
and determines where to send it. Now, the resulting route
typically points to a controller/action
that might execute some logic. For example, that logic might include accessing a mongo database that retrieves a stored twitter id and access token. The id and token are then used to request additional details of the user from the Twitter API, all before the server pre-processes a template which contains tags with variables that are replaced by the Twitter details into a View
. The View
is then sent back to the client and ultimately rendered by the browser.
Now, this traditional approach to web applications has at least two weaknesses. First, its reliance on the server for page creation means the responsiveness of the app is impeded by the constant round trips necessary to update changes to the View
from the server. Second, is that the API being tightly coupled to the View
makes it less flexible for other potential consumers to use.
A more modern approach to web applications solves both weaknesses by pushing the responsibility for changing the UI to the client as well as decoupling the API to act as an independent provider of endpoints. These endpoints can then be accessed by the browser UI, a native app UI, a mobile UI, or even a smart refrigerator UI.
Now this doesn’t mean that we’ll be abandoning Server Rendered Views entirely. Instead activityOverlordv2.0 will take a hybrid approach using a blend of Server Rendered Views combined with Front-end Framework Components to deliver a UI that makes authentication and SEO manageable. But as usual I’m getting ahead of myself.
So let’s go back to the project and see what Server Rendered Views look like in action. By default, Sails generated three files — homepage.ejs
, layout.ejs
, and a route to the homepage contained in /config/route.js
. So when I make a request in the browser to localhost:1337, the Sails router looks at the request and matches it with the route
in the routes file (e.g. route.js
). This triggers the View Engine to pre-process layout.ejs
with homepage.ejs
to produce the View that’s being rendered by my browser. The Layout.ejs
file is actually a wrapper around homepage.ejs
. And what I mean by that is if we look at both files you can see that layout.ejs
contains typical mark-up that we want for every page, things like DOCTYPE, html, and head tags. So we have EJS tags here with a body
variable which when processed by the Sails View Engine is replaced with the contents of homepage.ejs
. The result is our View.
Despite it’s power, and to make this example crystal clear, I’m going to disable the layout functionality in Views at least for the time being. To do this I’ll go into into /config/views.js and change the layout
parameter from layout
to false.
The first part of activityOverlord v2.0 consists of the Signup Page and when completed will look something like this. Let’s get started. I’ll first create a new file named startup.ejs
and I’ll put a simple header in it <h1> Signup Page</h1>
. Next, I’ll remove the existing route
to the homepage
and replace it with a route
to the new The Signup Page
:
1
|
|
Let’s take a look. I’ll start Sails using sails lift
and then navigate my browser to localhost:1337/signup
. Okay good, here’s our signup page.
Let’s go back to the Signup Page
and I’ll start by copying in some boilerplate html
. Next, I’ll add our first Angular directives
into the body tag — ng-app
, ng-controller
, and ng-cloak
. Let’s refresh the browser and see what happens. Well nothing happens, because we haven’t yet added Angular yet. We could manually add the script tags that reference Angular, however, Sails can do this automatically for us using it’s own tagging system (e.g. <!--SCRIPTS-->
tag). You know what, it’s easier just to show you. So I’ll add the tags to signup.ejs
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
I’ll restart the Sails server using Sails lift
and then go back into the browser but also open up the browser console…okay let’s refresh the page and see what happens. Okay, that new, we get this console message that we’re “connected to Sails”, but where did that come from? If we look at the page source we’ll get a clue. Towards the bottom of the page a link to a file namedsails.io.js
has been added to our signup page (e.g. signup.ejs
). But where did that link come from? Let’s go into our project in Sublime and navigate to assets/js
. In the dependencies foler there’s our sails.io.js file. First, this file deals with web sockets
and socket.io
, which we’ll be covering in later episodes. The big question now is, how did a link to that file get inserted into signup.ejs
? The short answer is that Grunt did it for us. Okay, so what’s Grunt
?
Grunt
calls itself a JavaScript Task Runner but really Grunt
is all about automation. It allows you to create repetive tasks that can be executed automatically. For example, there’s a Grunt task that’s looking for changes in the Sails assets folder. Sails creates a .tmp folder in the root of our project. As we can see here any files in the assets folder are copied into .tmp/public which acts like a static file folder you’d find on any web server. But what about the link to sails.io.js? This is yet another Grunt Task that will place a link to any javascript file found in assets/js that has the corresponding Script tags we just placed in signup.ejs (e.g. <!--SCRIPTS--><!--SCRIPTS END-->
).
Of course all of these tasks are happening automatically without us having to be aware of them. However, for those of you who want a little more detail behind the magic, I created a doc that goes into much more specifics of the Grunt/Sails integration than I do in this screencast. The documentation can be found here.
So let’s go ahead and place the main Angular file in the assets/js/dependencies
folder and I’ll refresh the browser. Looking at the page source we can see that a link to angular has been automatically placed in our signup page. But when I refreshed the browser, Angular is mad at us because we have some directives in the signup page without any associated javascript files. But not to worry, we can easily fix that. Let’s navigate back to the assets/js
folder in Sublime and we’ll create a folder called public
. We’ll put all of our Angular files that have to do with those parts of the application that don’t require authentication. Within public
we’ll create a folder called signup
. And within signup
we’ll create two files SignupModule.jsand
SignupController.js. Okay, first let's take a look at
SignupModule.js` and I’ll add the bare bones Angular code to define our new module.
1
|
|
Next, I’ll open SignupController.js
and add the bare bones Angular code to define our new controller.
1
|
|
Let’s go back to the browser and refresh the page. As you can see, we still have an Angular issue. If we look at the page source, we can see what the issue is, SignupController
is getting loaded before SignupModule
. This is easy to fix and touches on another aspect of Grunt. Most of the configuration for Grunt can be found within the activityOverlord20/tasks
folder. For this issue we’re going to look in the root of the tasks
folder for a file named pipeline.js
. This file contains the configuration for how the SCRIPTS tags are used (e.g. <!--SCRIPTS-->
) as well as some other tags we’ll be using shortly.
Next, I’ll open pipeline.js
in Sublime. We want to load the SignupModule
after dependencies but before any other javascript files. So we’ll put the path to our file here.
1 2 3 4 5 6 7 8 9 10 11 |
|
Now when I refresh the browser, Angular no longer complains and if look back at the page source we can see that SignUpModule is being loaded before SignupController.
So let’s go back to signup.ejs in Sublime and insert the remainder of the markup for our Signup Page. Let’s start-up Sails using Sails lift
and navigate the browser to localhost:1337/signup. Although the page loads without errors, there’s some obvious dependencies that need to be added in order for this page to look right. First I’ll add Bootstrap (e.g. bootstrap.css
) to the /assets/sytles
folder. Similar to what we did with javascript files in the previous episode, Sails will automatically include links to the CSS in signup.ejs
. Taking a look at signup.ejs
we can see the Styles tags that were added when I brought in the earlier mark-up as well as a link Grunt automatically created to the bootstrap file I just added.
1 2 3 4 5 6 |
|
Next, I’m going to create a fonts
folder and add some fonts we’ll be using later in the interface.
I’m also going to add some .less
files into the assets/styles
folder that we’ll being using later in the interface as well.
You may have noticed the importer.less
file which let’s us control which less files are included the Styles Tag as well as their order. Note that the Grunt task included in Sails will only compile the .less
files that are referenced in this file.
I’m also be adding Jesús Rodríguez’s https://github.com/Foxandxss fantastic angular messaging library Angular-toastr. The library contains both a javascript and a css file.
Finally, I’ll add the CompareTo
Angular Directive which will help us with comparing the value of form fields. I’ll place it in the /js/dependencies
folder.
So let’s see where we are after these dependencies were added. I’ll head back to the browser refresh the page. The console displays an Uncaught reference error that Angular is not defined.
Since this is coming from the toastr
library my hunch is that we have a loading order error. And sure enough if we look at the page source, the toaster
library is being loaded before Angular. I’ll head back to our project and pipeline.js
in sublime and add a reference to Angular here (e.g. 'js/dependencies/angular.1.3.js',
) that will load Angular first before any of the other depedencies.
Let’s head back to the browser and refresh the page. Great, there’s no longer and error.
Now let’s take a look at some form validation in signup.ejs. There are three components to the form field validation we’re going to perform. So taking a look at the name field. We first use the ng-class directive to create the has-error
class if the field name is invalid and dirty. This will insert a red border around the input field. Next we’ll configure the validation parameters of the field itself. In this case we’re requiring that the field have a value as well as have a maxlength of 50 characters. Finally, we’ll set-up the text for our error message here.
1 2 3 4 5 6 |
|
Let’s see this in action. I’ll go back to the browser and type in a name. Now If I remove the name, the required validation is triggered and if I add to many character’s the maxlength validation will be triggered.
Let’s go back to signup.ejs and review the remaining form fields configuration.
So the Title field’s configuration is identical to the name, the title is required and has a max length of 50 characters.
1 2 3 4 5 6 7 8 9 10 11 12 |
|
The Email field is required and requires a properly formatted email address.
1 2 3 4 5 6 7 8 9 10 11 12 |
|
The Password field is required and must be at least 6 characters. Also notice that we’re using the CompareTo directive to compare the password field with the confirmPassword model.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
The Password Confirmation field is required and must match the Password Field.
1 2 3 4 5 6 7 8 9 10 |
|
Finally, we disable the form submission button via the ng-diabled
directive until all of the form fields have valid values.
1 2 3 4 5 6 7 8 |
|
Okay let’s go back and take a look at the validations in action. I’m going to refresh the browser.
Believe it or not that rounds out our signup page. In the next episode we’ll start to flesh out our initial API that connects the signup page with an endpoint that creates a user account via a model into a database.
You can find all of the source code for this episode at the activityOverlord20 repo on github.
And If have a moment plese follow me on twitter and be sure to signup for the Sailscasts mailing list so I can finally prove to my wife that there are actually folks watching this stuff. As always thanks for watching.
]]>Follow me on twitter here.
Subscribe to the sailscasts mailing list here.
Howdy and welcome back.
As in the previous series, I’m going to assume you already have Node installed. If you don’t there’s a bunch of helpful documentation on nodejs.org and installers for both Mac OS X and Microsoft Windows can be found here as well as binaries for Linux and SunOS.
As for as my development enviornment, I’m using OS X v10.10.1 also known as Yosemite. For my text editor I’m using Sublime Text 2 which can be found here. There’s also an update to the editor, version 3, which I’m not currently using but can be found here. Another tool we’ll be using is the quite awesome chrome extension, known as – POSTMAN which can be found here.
And last but certainly not least I’ll be using Sails v0.11.0. Now, you may see me running some release candidates of Sails initially, however, because the release of Sails v0.11.0 is eminent, I’m not going to bother with how to install a release candidate and these initial screencasts are really not effected by the currently published version of v0.10.5. Okay let’s get Sails up and running by heading over to the terminal window.
From the last series, I received comments from a bunch of folks who had a real aversion to the terminal window. For those of you who are new to back-end programming, jumping into the terminal window might seem intimidating. Let me start by saying if I can understand it, believe me you can understand it. By the end of this series you’ll wonder how you got through your day without multiple visits to the terminal. For those of you on OS X you’ll find the terminal application is somewhat hidden in /Applications/Utilities/Terminal.app
. One other potential point of confusion about the terminal is how it’s referred to. For example you’ll hear terminal window, prompt, command-line, shell, etc. For what it’s worth here is my attempt at terminology superimposed on this thing I’ll be calling the terminal.
With that said, let’s jump in.
To install sails, you simply type npm install sails -g
, where the -g stands for global. Note, that because of the way ownership rights are set up on my machine, I have to use sudo
or super duper user, actually according to wikipedia it means substitute user do
….something like that. Regardless, it allows you to temporarily issue a command as a super or root user. After that Sails will start installing, so see ya in second.
So, in the last series of building activity overlord some people were confused about the distinction between installing sails globally and where Sails is installed when creating a new project. By installing Sails globally, we can have access to command-line tools from anywhere on the command-line.
For example, let’s create the initial Sails project for activityOverlord v2.0. From the terminal I’ll type sails new activityoverlord20
.
So what just happened? Sails used the globally installed version of itself to generate all of the necessary initial structure for our application. This includes installing a copy of Sails itself inside the root of our application. That way, sails applications are completely self contained…there’s no “other software” outside of our project that we’re dependent upon.
Throughout these screencasts there will be times that I want to address specific changes that effect the previous activityoverlord screencasts with v0.11.0 of Sails. So if you don’t have previous experience or interest with older versions of Sails, now is the time to pick up that musical instrument or check some texts for the next few seconds. When I created the new Sails project you may have noticed that I didn’t use the --linker
prarameter. That’s because there’s no longer a requirement to use it. By default, that functionality is built into every Sails project.
Now, let’s go back to the terminal and move into the new project by typing cd acitivtyoverlord20
. Without changing any of the initial files or folders of the app I can start our newly created web server by typing sails lift
and Sails confirms that it is indeed up and running. Next, I’ll open a browser at localhost:1337. The browser is accessing the default home page Sails generated when we initially built the project.
It’s important to take a moment here and realize what we’ve just accomplished. In just a few commands we’ve built the initial infrastructure of a web application which includes the creation of its own web server. We then have a browser, known as the client that is making a request for a previously generated home page through the Sails web server. The Sails server then response with the home page to be rendered in the browser.
Some of you may be confused by the address localhost:1337
. Typically, when you browse the web you’ll enter a domain name like google.com
…and that name actually resolves to an some ip address which maps to some computer out on the internt. Alright, localhost
is default name for what is technically termed a loopback address
. My machine’s localhost
points to the address 127.0.0.1
. What the loopback address
allows allows us to do is bypass the outside network and address a specific web service on our local machine. In our case that web service is the Sails server. So when the Sails starts or lifts, by default, it’s given a port number of 1337
. As we’ll see in a second we can change the port number
if we want. Port numbers
are the way in which we can differentiate one web service from another. For example, I currently have our activityoverlord20 project running on port 1337
. I created another Sails project called foo
that I’m going to lift on port 1338
. When I go into the browser and open the address localhost:1338
the homepage that I altered comes up for the foo project. So the combination of localhost and the port number allows us to run both the server and client on the same machine while we’re building the project. To get completely geeky, and show you that localhost is really an arbitrary, there’s a file found on my mac at ~/etc/host
that specifies what “points” to 127.0.0.1
. As you can see, in addition to localhost I have the name yaya
also pointing to 127.0.0.1 and yes we can go into the browser and type yaya:1337 and our homepage comes up.
Okay, I think I’ve beat that one to a pulp…but sometimes you know I just get carried away. In the next episode we’ll start building up the client ui of activityOverlord v2.0. So, see ya’ll in a bit.
]]>Follow me on twitter here.
Subscribe to the sailscasts mailing list here.
Howdy all…Happy New Year, and welcome.
Well for those of you who have listened to the previous activityOverlord screencasts the good news is I am armed with Keynote v6.5, which is filled with even more cheesey animations for me to abuse, the bad news is I’m even older than I was in the last series of screencasts. Seriously though I think I can overcome that and take you on another programming journey, this time updating Activity Overlord to v2.0. Now what’s in v2.0? The major updates include using a bunch of new features in v0.11 of Sails as well as updating the front-end UI to use AngularJS.
The current plan is to get the the original activityOverlord videos up to date ASAP. Time permitting I’ll continue making screencasts that take deeper dives into various concepts addressed in building the app. The videos will be hosted on youtube under the ponzicoder channel with transcripts available at the following gihub page. The source code is hosted in a repo on github here. If you like what you see and want to make an old guy happy, please follow me on twitter here and as important subscribe to the sailscasts mailing list here.
If you find bugs feel free to send a pull request to this github repo — http://github.com/irlnathan/activityoverlord20. If what I just said makes absolutely no sense, I’ll be covering git and github in future screencasts. If you have questions, there are a variety of forums to ask them. The Sails irc channel, Stackoverflow using the tag sailsjs, and the Sails google group are all active.
Needless to say I’m really excited about this new series…and as always thanks for watching.
]]>The repo for this episode can be found here: https://github.com/irlnathan/sails-crud-api-sailsAnswers8
Howdy and welcome back. You’re watching the third and final installment of our three part series. In part one we learned how the http request/response protocol works with routes, controllers, actions and models to deliver a restful json CRUD api. In part two we took those concepts and built the api from scratch.
In this episode we’ll explore Sails blueprint: actions and routes, a powerful combination of functionality that are often used but not always fully understood. The goal of this episode is to show you what the blueprints are all about and how to use them to make your programming life a bit easier. So where are we going to start?
First a small warning here. This episode relies heavily on the previous two screencasts, so if you start to experience dizziness, tightness in the chest, or depression, I highly recommend you review those screencasts.
Let’s take a look at the roadmap of what we’ll be covering in this episode.
There are four pre-built blueprint: actions (e.g. find
, create
, update
and destroy
). As we’ll see in a minute, these four actions map directly to the CRUD api we buit in the last episode. There are also three blueprint: route types, blueprint: actions routes, blueprint: rest routes, and blueprint: shortcuts routes.
I want to make one thing very clear from the beginning. Blueprint: actions and blueprint: routes are virtualized in the sense that they are not explicitedly defined in controller files like SleepController.js or the routes.js file. Instead, they’re built-up when sails starts using sails lift
. But as usual I’m getting ahead of myself.
Let’s start with seeing what happens when we combine blueprint: actions and blueprint: rest routes.
So in the last episode I went over in excrutiating detail, how to make a restful json CRUD api. The goal of api was to track our sleep patterns, specifically how much we sleep each night and the quality of that sleep. The best way to show you how blueprints help you automate the creation of an api is to repeat the process of building the restful json CRUD api, however, add the power of blueprints.
Let’s jump in here and create a new mySleep project. I’m in the terminal and will create the project using sails new mySleep --linker
with the linker flag. Next, I’ll change into the mySleep folder and create a sleep
model and controller using sails generate sleep
. Finally, I’ll start the project using sails lift
. Okay, we’re done, the api is complete.
But you DON’T believe me? After all we’ve been through. Well I guess I’ll have to prove it to you.
Seriously though, everything we did in the last episode was just built with those three commands.
I’ll open a browser and once again using the POSTMAN chrome extension I’ll make similar requests that we made in the last episode that relate to a CRUD api…
Let’s start with the Create portion of CRUD. I’ll add an instance to the sleep model using the http verb POST
to the path /sleep
adding two parameters hours_slept
and sleep_quality
. After sending the request the api returns our newly created record as json. In fact, I’ll make four more model instances inserting different values for hours_slept
and sleep_quality
.
Next we’ll try the Read portion of CRUD. Let’s get all of the model instances by making a GET
request to the path /sleep
. After sending the request the api returned all five instances of the model:
Next, let’s make a GET
request to the path /sleep/2
. The api returns a single instance of the model with an id
of 2:
Now let’s try a request with some criteria. We’ll look for any model instances with an id
not equal to 4, limited to 3 model instances and in descending order.
After making the request, the api returns three instances of the model in descending order.
Things are looking up for my assertion that our api was indeed complete.
Next up, we’ll try the Update portion of CRUD api. I’m going to make a put
request to the path:
http://localhost:1337/sleep/3?added_attrib=12
After making the request, the api returns our instance of the model that has an id of 3
with our added attrib formatted as json.
Finally, we’ll try the Delete portion of our CRUD api. Once again within the POSTMAN chrome extension I’ll make a delete request to the path:
destroy
http://localhost:1337/sleep/5
After sending the request the api responds with the model instance it just deleted formatted as json.
Let’s take a quick check of the controller we created in controllers/SleepController.js
. Yep, nothing in it. Next let’s look at the config/routes.js
file. Pretty much the same, nothing in there except the home
route.
So at this point your probably wondering how in the heck are we able to use the same restful api end points that we used in the last episode BUT without any explicit actions are routes?
Is it mind control?
The really short answer is that we’re using blueprint: actions and blueprint: rest routes to automate the process of building the api. The next question is, how do they work?
When sails initially starts using sails lift, sails looks to see if you have any controller’s defined. In our example, we have one controller, the Sleep controller. Sails then provides access to blueprint: actions for this sleep controller as if we built them in the controller ourselves. Sails also automatically creates blueprint: rest routes that are identical to the routes we explicitedly created in the last episode. When combined together, the blueprint: actions and blueprint: rest routes, give us the exact functionality we had in our manually created json restful CRUD api without having to create anything other than a project, an empty controller, and an empty model.
So where are the blueprint: actions actually defined? Okay, nerd alert here, and when I say nerd alert, I’m starting with myself, you can see what the actions look like by taking a look at the sails source here on github. Looking at these actions they look very similar to the ones we created in the last episode. The difference is that sails handles all of this in the background, creating the necessary blueprint: rest routes that connect the actions to our controllers automatically. Again, this is all happening in memory without you having to explicitedly create anything.
Also, remember, that the mySleep project we’ve been working through uses a single controller. The blueprint: actions and blueprint: rest routes are not limited to a single controller so for a more complex project, with multiple controllers, sails blueprints: rest routes are built automatically for all of the controllers.
You can of course override any of the actions by explicitedly creating one of the actions in your controller. For example, I’ll go back to the SleepController, and create a find action that simply responds with “I’m your new explicit find action” upon request. Let’s restart the server and go back into the browser. I can still use the create
, update
, and delete
actions, however, now when I make a get
request to the path /sleep, I now receive our message .
So with sails you get the blueprint: actions and the blueprint: rest routes but wait there’s more! Sorry, couldn’t resist that. In addition, sails also provides blueprint: action routes, not to be confused with blueprint: actions.
blueprint: action routes speed up backend development and shorten the development workflow by eliminating the need to manually bind custom controller actions to requests through routes.
So when sails starts via sails lift, sails analyzes your controllers and if it finds an explicit action in a controller, it will bind GET
, POST
, PUT
, and DELETE
routes to the explicit action. For example, in our sleep project, I’ve added the action query
to the sleep controller. So when sails starts, using sails lift
, sails will automatically build the following blueprint: actions routes:
'get /sleep/query/:id?: UserController.query'
'post /sleep/query/:id?: UserController.query
'put /sleep/query/:id?: UserController.query
'delete /sleep/query:id?: UserController.query'
Let’s see it in action. I’ll open a browser and make a get
request to the path /sleep/query
. The query
action responds with a view that can be found in views/sleep/query.ejs.
The important take-away here is that when I created a new action, in this case, the query
action, I didn’t have to create a route to bind a request to that action. By using blueprint: actions routes sails did this for me automatically.
So let’s go back to our blueprint: roadmap. So far we’ve covered blueprint: CRUD actions as well as blueprint: rest routes and blueprint: actions routes. The final blueprint: route type is blueprint: shortcuts.
blueprint: shortcuts build routes that allow you to use the blueprint: actions from a browser. I use blueprint: shortcuts during development as a handy way to manipulate my underlying model.
The best way to see how this works is through an example.
I can grab a list of all my model instances using the url /sleep
. I can create a new model instance using the url ‘/sleep/create’ and adding the parameters sleep_quality and hours_slept. I can also update that same instance using the url /sleep/update
with the id
of 8
and changing hours_slept from 10
to 9
. Finally, I’ll delete the model instance by using the url /sleep/destroy/6
.
One bit of caution. blueprint: shortcuts were not designed to be used in production. So how do we disable parts of the blueprints?
The blueprint: routes
and blueprint: actions
are completely configurable. That is, they can be disabled simply by setting the values of actions, rest, and/or shortcuts to false
in the \config\controllers.js
file. You can have even finer granularity by setting the same values in the _config
object within each controller which will override what is in \config\controllers.js
file.
Okay, we have a bunch of routes here, but how do they all fit together. So all of these different routes have an order of precedence or rank. When a request comes in sails first checks the explicit routes in routes.js. Next it will look to see if there’s a match in the blueprint: actions routes, followed by the blueprint: rest routes and finally, sails will see if there’s a match in the blueprint: shortcuts routes.
So if there’s a get
request to the root route, sails will route it via routes.js
to render views/home/index.ejs
.
If there’s a get
request to /sleep/query
, sails, finding no match in routes.js
, will look to the blueprint: action routes and finding a match will route the request to the explicit query
action of the Sleep Controller.
If there’s a delete
request to /sleep/5
, sails, finding no match in routes.js
or blueprint: action routes, will look to the blueprint: rest routes and finding a match will route the request to the blueprint: destroy action, returning a json object if successful.
Finally, if there’s a reqest to get
/sleep/update/2
with some params, sails, finding no match in routes.js
, blueprint: action nor rest routes, will look to the blueprint: shortcut routes and finding a match will route the request to the blueprint: CRUD update action, passing any params to the model’s update
method returning a json object if successful.
In this series we’ve learned the concepts of how to use the http request/response protocol with routes, controllers, actions and models to deliver a restful json CRUD api. Using those concepts we’ve built the api from scratch. Hopefully, after this episode it becomes apparent that blueprint: actions and routes are really about automation. That is, eliminating the necessity of writing, at least initially, repetitive actions and routes during development.
We’ve covered a lot of material and I hope you’ve found it helpful. As always thanks for watching and if you get a chance follow me on twitter at irlnathan.
]]>The repo for this project can be found here: https://github.com/irlnathan/sails-crud-api
Howdy and welcome back to part II of our three part series. In the last episode we learned how the http request/response protocol works with routes, controllers, actions and models to deliver a restful json CRUD api. In this episode we’ll take the concepts we learned and use them to build the api from scratch. In the final episode we’ll explore how sail’s blueprints: actions and routes can be used to create that same restful json CRUD api automatically for any of your controllers and models.
Let’s review what we’re trying to accomplish.
Our api will be used to access and update information that tracks our sleep patterns including how much we sleep each night and the quality of that sleep.
So we want the api to be able to respond to requests to find, create, update or delete instances of our sleep model. We’ll create actions that corresond to the requests and then build up routes that match the appropriate http verbs and paths with the corresponding controller and action.
get
with the path /sleep/:id?
and bind to the sleep
controller and find
action.post
with the path /sleep
and bind to the sleep
controller and the create
action.put
with the path /sleep/:id?
and bind to the sleep
controller and the update
action.delete
with the path /sleep/:id?
and bind to the sleep
controller and destroy
action.The actions will then use the model methods to find, create, update or destroy the model as requested and use the parameters hours_slept
and sleep_quality
to pass any necessary information within the request through the action to the model. The action will then respond with the request status as well as any model instance or instances required.
So let’s get started. I’m going to bring up a terminal window and we’re going to create a new sails project called mySleep
using sails new mySleep --linker
. and I’ll change into the mySleep
folder and generate a sleep controller and model using sails generate sleep
.
So, here’s a roadmap of what we’re going to build. I’m going to start with the create action, building the action and then building the route that will bind the find request with the sleep controller and find action. I’m going to go through each action, create it, and then build the matching route that will bind our request to the controller and action. So let’s start with the create action.
I’ll open my sleep controller found in /api/controllers/SleepController.js
and create my first action called create
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
The action is straightforward, we’re going to grab the request’s parameters in the var params
and then pass params
into the create
method of our sleep
model. If there’s an error we’ll return it and if not I’ll send a 201 status code response with the newly created model instance formatted as json.
So that’s the create
action, now I need to create a route that will bind this controller and action to our request. So let’s open the routes in /config/routes.js
and I’ll add my route after the existing home route:
1 2 3 4 5 6 7 8 9 10 |
|
The route consists of the verb post
to the path /sleep
which is bound to the sleep
controller and the create
action. So let’s make sure our create action is working. I’ll go into the terminal, start sails with sails lift
. I’ll again be using the POSTMAN chrome extension to test our requests. We’ll be using the http verb POST
to the path /sleep
adding two parameters hours_slept
and sleep_quality
. When I click send, Sails returns my newly created record as json.
1 2 3 4 5 6 7 |
|
So let’s take a look at our api roadmap. We’ve built the create action as the first of the four actions of our api. Next, we’ll build the find
action and then we’ll build a route that will bind Sleep controller and find action to our request. For the action let’s go back into the SleepController.js
file and look at the find action code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
|
Let’s also take a look at the route that will bind our request to the sleep controller and find
action in /config/routes.js
:
'get /sleep/:id?': 'SleepController.find'
The route points to our find
action but look the end of the path, what’s up with :id?
, and the question mark? The question mark makes the id
parameter optional. That way we capture both the request 'get /sleep'
as well as 'get /sleep/:id'
.
The find action will be our most complex action of the four in our api. This is because we have to provide for a request finding a single instance of the model, multiple instances of the model, as well as using criteria and options to narrow and/or limit the scope of the find request.
So within our find action, we’ll attempt to assign a parameter called id
to the var id
. The next line of code looks to see if the id
is a shortcut. I’m going to skip over this part because shortcuts are part of sail’s blueprints which we’ll be discussing in the third episode.
So if the id
exists we’re going to assume that the request is looking for a particular model instance. We’ll pass the id
to the findOne
model method and if we don’t get back an instance of sleep
in the callback, we’ll return or respond with a status code of 404
— not found
. On success, we’ll return and respond with the model instance formatted as json.
Checking for multiple model instances. If no id
is provided we’ll start looking for other criteria or options that may have been passed as a parameter for finding one or more model instances. Criteria is placed in a where
clause which is just the key name for a criteria object. For example, if your want to find all model instances where sleep_quality = good, your parameters would look like this: ?where={sleep_quality: "good"}
. We’ll also check for options that further limit the result in some way. For example, let’s say we only want the first 5 model instances of our result. The parameters would look like this: ?where={sleep_quality: "good"}&limit=5
.
So if where
exists as a parameter and the value for it is a string, we’ll just parse it as json and assign it to the var where
. Even if where
doesn’t exist we’ll still look for the keys limit
, skip
, and sort
and place them within the options object. Finally, we’ll pass the options object to the Find model method and if we don’t get back an instance of sleep
in the callback, we’ll return or respond with a status code of 404
— not found
. On success, we’ll return and respond with the model instance(s) formatted as json.
So we have the find action complete, let’s make sure all of this works. I’ll head back to the terminal and restart the sails server using sails lift
and then open a browser with the POSTMAN chrome extension. I’ve added a few more instances of our sleep model. Let’s take a look by sending a get
request to the path /sleep
. After sending the request the api returned five instances of the model:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
|
Since we didn’t provide an id
or any criteria or options, the api used the find model method and returned all instances of the model formatted as json.
Next, let’s make a get
request to the path /sleep/2
. After pressing send, the api returns a single instance of the model with an id
of 2:
1 2 3 4 5 6 7 |
|
Now let’s try a request with some criteria. We’ll look for any model instances with an id
greater than 1:
1 2 3 4 |
|
After making the request, the api returns four of the five model instances with id
’s greater than 1.
Finally, I’m going to combine the criteria with some options. I’m going to make a get
request to the path /sleep
for model instances with an id
not equal to 4, limited to 3 model instances and in descending order.
1 2 3 4 |
|
After making the request, the api returns three instances of the model in descending order.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
Now that we know that our find
action is battle tested, let’s go back to our api roadmap. By building the create action and route and the find action and route we’re half way through our api. Next, we’ll build the update
action and then we’ll build a route that will bind the Sleep controller and update action to our request. Let’s head back into the SleepController.js
file and look at the update action code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
The update action consists of finding the id
of the model instance to update coupled with the criteria that will be updated. If there’s no id
as a parameter we respond with a 400 status— ‘No id provided’. Next we attempt to update the model instance using the id
and criteria provided. If there’s an error we’ll return it and if not respond with the updated model instance formatted as json.
So now that we have the update action complete, we’ll bind that action to the request forming a new update route:
'put /sleep/:id?': 'SleepController.update'
The route points to our update
action and uses the same :id?
pattern that we used in the find
route.
Let’s make sure all of this works. I’ll restart the sails server using sails lift
and then open a browser with the POSTMAN chrome extension. I’m going to first make a put
request to the path:
http://localhost:1337/sleep/3?added_attrib=12
After making the request, the api returns our instance of the model that has an id of 3
with our added attrib formatted as json.
Next, I’ll make a put
request to:
1 2 3 4 |
|
…but instead of using query parameters, I’ll pass the update via the request body. After making the request, the api returns our instance of the model that has an id of 3
with our added_3 attribute formatted as json.
1 2 3 4 5 6 7 8 |
|
Now that update action and route is complete it’s time to build the last action of our api the destroy
action and then bind it to our request to form the delete
route. Let’s head back into the SleepController.js
file and look at the destroy action code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
So we’ll attempt to assign the id
param to a var called id
. If it doesn’t exist I’ll return a 400— ‘No id provided’. If an id
parameter was provided in the request, I’ll attempt to find it in the sleep
model. If the model doesn’t exist I’ll respond with a status code of 404
— not found
. If the mode instance does exist, I’ll pass the id
to the destroy method of the model returning either an error if any, or the deleted model instance formatted as json.
Next I’ll bind the destroy action with the request in its own delete
route:
'delete /sleep/:id?': 'SleepController.destroy'
Let’s check it out by restarting the sails server using sails lift
. Once again within the POSTMAN chrome extension I’ll make a delete request to the path:
destroy
http://localhost:1337/sleep/5
After sending the request the api responds with the model instance it just deleted formatted as json.
Congratulations, you’ve built a restful json CRUD api. Any client-side device that supports http requests can now hit our api’s endpoints and request and submit information about our sleep model.
In the next and final episode of this series I’ll show you how sail’s blueprints: actions and routes can be used to create this same restful json CRUD api we just created, automatically for any of your controllers and models.
Okay that was a lengthy one, As always, thanks for watching and if you get a chance, follow me on twitter @irlnathan.
]]>Howdy and welcome back. This episode is the first of a three part series. We’ll start with how the http request/response protocol works with routes, controllers, actions and models to deliver a restful json CRUD api. By the end of the second episode you’ll use these concepts to build a restful json CRUD api from scratch. In the third episode we’ll explore how sail’s blueprint: actions and routes can be used to create that same restful json CRUD api automatically for any of your controllers and models.
In lieu of being able to download this episode directly to your brain matrix-style, I’ll instead provide a bird’s eye view of all of the concepts we’ll cover. Nothing is more confusing than trying to learn something new in the abstract. So let’s begin with a tangible scenario for our restful json CRUD api. Our api will be used to access and update information that tracks our sleep patterns including how much we sleep each night and the quality of that sleep.
So, we want our client-side device to be able to make requests to a our api running on a server and for that api to respond to our client-side device’s requests. I’m using the term “client-side device” very loosely here because the device can reside on your lap, your phone, or even your refrigerator. If it can make a request it can use our api regardless of the type of client-side device.
So, what types of requests will we be making to the api? The request will interact with the api to either find, create, update or delete information about our sleep. The api will in turn be listening for our request and using a router, routes, controllers, and utlimately actions to interact with our sleep information in something called a model
. The action will then respond to our client-side device with the status of our request and any additional information necessary to fulfill the request.
Now let’s break-down each concept in detail. First let’s strip down this diagram to the basics.
So how do request and responses work? The request and response are part of the http protocol. Don’t let the term protocol throw you. It’s just a set of agreed upon rules that make it possible for different types of devices to communicate together across a network.
So, why are we starting with the http
protocol? Well, you’ve got to start somewhere and although we could have started at ohm’s law and worked our way up levels of abstraction to http
, this would take an awfully long time and more importantly my knowledge once we go below the level of http
drops rapidly to pretty much zero. As a tangent, in case you are a complete nerd like myself and would like to learn more about the very low levels of abstraction, there’s a great MIT course 6.002 available on youtube which introduces the fundamentals of the lumped circuit abstraction. But for our purposes anything below the http
protocol we’ll just say consists of turtles all the way down.
Recall that our api will support requests to find, create, update, or delete sleep information.
But how will our api differentiate the requests? That is, how will the api know that we want to find versus create, update versus delete a set of sleep information?
The http
protocol provides the means to accomplish this via the use of http
verbs – get
, post
, put
, and delete
and each of the http verbs line up with the type of request.
Most likely you use http verbs every day. Each time you use your browser to open a web page, that browser is making a get
request on your behalf.
As a quick aside you’ll often hear the term CRUD functions when working with a web api. CRUD stands for create, read, update and delete each of which match up nicely to our http verbs and actions.
The verb is the first of three essential parts of our request. The other elements are the path, which relates to our controller, and we’ll talk about in the next session and finally optional parameters which are just additional pieces of information we might want to send in our request.
The important take away here is we can now combine http verbs and paths to convey our intent to the api. By using 'post /sleep'
the api knows ahh, create a new instance of sleep and by passing it parameters the api knows that we want to use those parameters as part of the sleep instance. How the api interprets this is the subject of the next section.
Before moving on though, you might be asking, how does my device generate a request. I’ve already said that when you request a web page in a browser you’re making a get
request. Also most programming languages have a library associated with http. In javascript, the jquery library uses $.get to make requests.
Let’s take a quick field trip to check out a request in action.
I’ll open a chrome browser which is using a chrome extension called POSTMAN. POSTMAN allows me to make http
requests using all of the verbs we were just talking about. Without it, browsers are limited to get
requests from the url window or get
and post
requests from an html form. I’m making the request to a completed version of our sails api, but don’t worry about that for now. I just want to show you an actual request in action.
So I’m going to make a request using the verb get
to the path /sleep
. And when I send the request via POSTMAN I get back all 5 instances of our model.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
|
So that’s it, that’s a request.
Now, by making the http
request to a server, we’re indicating that some bit of code should be executed on the server as a result of making the request. The chunk of code that is executed is known as an action and these actions can be grouped together into a controller.
But how do we link the request to the controller and action? Well, that’s where routes come in.
Routes are the instructions that tie the request to the controller and ultimately to the action.
So from our earlier example, the route consists of the verb— post
, the path— /sleep
which forms the request and pairs it with the sleep controller and the create action.
Although not part of the route per se, as we’ve already seen parameters can also be part of the request and these parameters can pass information to the action.
So let’s review, we now understand how the request and response works with the http protocol, that the router uses routes to tie the requests to the controller and action.
So what does the action actually do? The action is where the code resides to find one or more model instances, create a new model instance, update an existing model instance, or delete a model instance.
What is a model? A model is a representation of the attributes that describe the data your api will be managing. Our sleep model consists of hours_slept, and sleep_quality and sails automatically adds an id, createdAt and updatedAt attributes. The sleep model is an object so in addition to model attributes like hours_slept, the sleep model also has methods like find, create, update, and destroy. These are the methods that are called within our actions. Now don’t be confused by the fact that the actions and model methods have the same name, they share the same name because the action name relates to the ultimate model method the action will use. As you’ll see when building the api in the next episode, the actions combine code necessary to complete the request including calls to the model methods.
Having this seperation between the model and the place where your data resides is important because you might want to store your model in a sql database at first but later move it to a mongo database. Or you might be using data that doesn’t come from a database but instead from some other api. That’s really the power of a framework like sails in that we can learn one way of finding, creating, updating and destroying our model, and then let sails worry about how it actually accesses and/or stores it at the level of a database or other data source. In fact, the sails community provides us with different adapters like posgresql and mongo from which we can pick and choose where our data ultimately resides at will.
What is the model versus model instances. Think of the model as the instructions for building something whereas an instance of that model is one of the things you’ve built.
So let’s finish this episode with looking at the response. Depending upon the request, our action is responsible for finding, creating, updating or deleting instances of our model. We’ve learned that the model methods are called by the action. The action then responds to the request with the status of the request along with any model instance or instances associated with the action.
If you’re new to this your head might start to spin a bit but don’t panic in the next episode we’ll use all of this conceptual knowledge to build our own restful json CRUD api.
if you get a chance, follow me on twitter @irlnathan and as always thanks for watching.
]]>Howdy and welcome back.
So I’ve seen a bunch of questions asking where should you put your assets in your sail’s project. In this episode I’m going to talk specficially about javascript and css assets.
So, Sails uses grunt to manage assets. And Grunt generically provides ways for you to automate repeititve tasks. Sails uses grunt for tasks like syncing files between a project folder structure and the server’s public folder, but as always, I’m getting ahead of myself.
The configuration of grunt
is based upon a file called Gruntfile.js
found in the root of your sails project. There’s a lot going on in this file, however, again I’m going to concentrate on the javascript and css assets.
When you first create a project, you have the option of using the --linker
flag. I’m going to actually create two projects, one with the linker flag and one without. Let’s compare the two. Both have an /assets
folder. But the project with linker, not surprisingly has a /linker
folder and that’s within the /assets
folder. In the linker example, an additional /templates
folder is also created.
USING the --linker
flag
1 2 3 4 5 6 |
|
NOT USING the --linker
flag
1 2 3 4 |
|
Okay, great we have a /linker
folder, but so what? Well, let’s add some javascript and css to both projects and see what happens. I’ve replaced the index.ejs
file in the `/views/home’ folder of both projects with some very basic mark-up.
1 2 3 |
|
Next, I added jquery to the /js
folder of both projects. I also added my own javascript file — custom.js
to the /js/
folder.
1 2 3 4 |
|
Finally I added custom.css in the /styles
folder:
1 2 3 |
|
If the javascript and css are being linked properly, this is what our page should look like.
The custom.js file appends some html to the div in the index.js
file. Then it will add a new class working
to the div which will trigger the css located in the custom.css
file.
Let’s take a look at the project where we used the --linker
flag first. I’ll start sails using sails lift
. The first thing you’ll notice is that a hidden .tmp
folder is created. This is the server’s public folder. Opening up this folder reveals files that have been sync’d with the /assets
folder. So we can see that jquery.js, custom.js, and custom.css files have all been copied into the public folder. Next, I’ll open up a browser and enter localhost:1337
. Our heading showed up, but something’s up with my javascript because the appended html isn’t showing up. Let’s take a look at the console. Okay, there’s our problem. Jquery isn’t being loaded. Let’s take a look at the page’s source. Jquery is there, however, it’s being loaded in the wrong order. You might be asking yourself, how, did those links make it into page in the first place. That’s where some addition Grunt magic happens. Let’s take a look at our layout.ejs file.
Here is where the javascript links have been placed. Notice the two tags SCRIPTS and SCRIPTS END.
1 2 |
|
This is where Grunt will place links to any javascript files placed in the /assets/linker/js
folder. Grunt will also do this for our css between thse two tags, the STYLES and STYLES END:
1 2 |
|
Now you may have noticed these socket.io.js
, sails.io.js
, and app.js
files that are being injected into layout.ejs
. Not surprisingly these files have to do with socket.io and sails, however, for the purposes of this screencast we can ignore those files.
But we still have this ordering problem with jquery. To fix this we can go back into the Grunfile.js
and towards the top of this file is where Grunt is cofigured to inject css and javascript into layout.ejs
. I’m going to put a path to jquery below some other javascript files but before this default “catch-all” link and I say catch-all link because as you can see with these astericks, grunt is going to look at the linker folder and any folder underneath the linker folder it’s going to look for any files with the .js ending. It’s going to take those files and place them in between these two tags.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
So now jquery will be loaded before any other javascript files located in the assets/linker/js
folder. Let’s restart the sails server and go back to the browser and reload the page. Great now links to both our javascript and css files are being injected correctly. And just to make sure let’s go back to the source. And yes, we can see here that jquery is now being loaded before custom.js.
So to sum it up, having the linker folder provides for the automatic injection of links to any javascript or css files that are placed in the ‘assets/linker/js’ or ‘assets/linker/styles’ folders. Grunt based upon Gruntfile.js injects the links in layout.ejs
.
Now let’s look at the project that doesn’t have a linker folder. When I start the sails server for this project using sails lift
, the hidden .tmp
folder is also created. The javascript and css files are copied into the public folder this time without the /linker
folder. I’ll open up a browser and again go to localhost:1337
.
This time, however, we don’t have the appended text from the custom.js
file nor the red color to the text from the custom.css
file. Why is that? If we look at the layout.ejs
file, you’ll notice that the STYLE and SCRIPT tags are missing. These are automatically inserted when using the linker flag. Therefore, grunt doesn’t have these tags to tell it where to put the javascript and css file paths.
We can “upgrade” this project simply by adding the tags in layout.ejs
. We also need to remove the paths that were initially added to layout.ejs when the project was created. Now let’s go into Gruntfile.js and insert the path to jquery like we did in the linker example. Finally we’ll need to move the css and javascript assets we currently have under a /linker
folder. Now when I start sails and open a browser, the appended html is displayed in red, just as we expected. Looking at the page source, the css and javascript were injected properly and in the correct order into the page.
I hope you found this helpful and thanks as always for watching.
]]>Howdy and welcome to another thought provoking, informative sailscasts answers. Okay, maybe that’s a bit of a stretch but welcome all the same. I’ve been asked a number of times how to implement a more attractive url system in sails…commonly using slugs.
Let’s take a look at an example. In activityOverlord when you’re on the user profile page, the url is something like this http://localhost:1337/user/show/5220fa7b8764043122000001
. The ending part here is a mongoid. And that id is not very human friendly. What would be better is to have something like the person’s username. I’ll be doing a separate episode incorporating attrative urls into activityOverlord, however, in this screencast I’m going to show you how to do it generically for any project.
So I’ll create a new project called slugsville by entering sails new slugsville --linker
with the linker flag. Next I’ll change into the slugsville folder and generate a user controller and model using sails generate user
. So, let’s take a look at the user model. I’m going to paste in attributes for name, company, email, and phone as well as an attribute called slug.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
Let’s switch to the user controller. I have a fairly generic create action that creates a user with params I’ll send through the Postman chrome extension and then we’ll return a json object. When the user is created, however, we need to put some logic in that will process the username removing any spaces and lower casing the string before saving the value into the slug model attribute. We’ll do this by adding a beforeCreate()
method to our User
model.
So going back to the User
model, I’ll add the beforeCreate()
method first chekcing whether name
exists and then assigning the slug
attribute the value of name with no spaces and all lowercase. Finally we’ll use the next() method to continue. Let’s see if that worked.
I’ll go into the terminal and start the sails server using sails lift
. Next, we’ll go into the browser and using the postman chrome extension, I’ll create a new user with the following attributes. And great both the user and more importantly the slug were created.
So now let’s use this slug as a route parameter. I’ll head over into the `/config/routes.js file located in the config folder where we’ll create two routes.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Both /:slug
and /user/:slug
will bind themselves to the profile action of the User controller. In the User controller, I’ll create an action called profile. Next I’ll grab the slug param and assign it to the var slug. I want to let anything we catch with the slug param that has a dot in it like image.png
to pass through without hitting our find method. That way we reduce the overhead of searching for a user for params we know are not a name. So if the param has a dot in it, we’ll return next() which will continue to the next piece of middleware (if any).
Next, we’ll try to find a user by the slug attribute passing in the slug variable we obtained form the param. If we don’t find a user, we’ll again return next()
. If we do have a user I’m going to pass the entire user object to the view, in this case profile.ejs
.
profile.ejs
is a simple view template that displays the user name, company, email, and phone. Finally, I’m going to go back to the User controller and add a foo action to make sure that my action blueprint routes still work. If your not familiar with blueprint routes, I’m currently working on episode explaining blueprint routes, the first of which is devoted to action routes. Here I’m just adding the action foo that will return a 200 response.
Okay, let’s see if all of this worked? First, I’m going to create a few other users within postman. Here’s a list of our users. I can still access them via the id using /user/1
. But now I can access them by their username either at /username
or /user/username
. I can also access my foo action.
I think that’s a much better approach. I’ve left a link to this project’s repo here if your interested and I hope that it was helpful and as always thanks for watching.
]]>Howdy, I was recently asked whether there was a way to create dummy or fake data for testing in a sails project. One way to accomplish this is using /config/bootstrap.js
. Although I can automate the creation of the users, I’ll still need some fake users for sails to create. Instead of manually typing in each user’s attributes, I found this really handy online tool called generatedate. You have a bunch of options of how you want your data generated. In this example I’ll select name, company, and email selecting the simple json format. I’ll copy all of these users and head over to the bootstrap.js
file.
I’ll copy our new users into an array called dummyUsers. Next I’ll use the count()
method on our user
model. If there are any records in the User model than I’ll call the callback and nothing will be altered in the database. However, if there is no data in the model, I’ll use the create()
method on the User model, passing in the array dummyUsers
and then calling the callback. Note that the boostrap.js
is run once each time sails is started via sails lift
.
So, let’s see if that worked. I’ll go over to the terminal and enter sails lift. Next, I’ll get a list of user by entering /user
into the browser. And there’s our hundred or so users.
I’ve posted a link to the github repo of this project and I hope this was helpful. Thanks as always for watching.
]]>Howdy and welcome back. Like most of episodes this one is going to move quickly. I do this so they don’t go on forever, however, I highly recommend stopping and rewatching parts that might go to fast. So, I thought it would be useful to go over how to deploy activityOverlord into the wild. This episode will cover deployment to heroku, a self-described cloud application platform.
Heroku makes it really easy to deploy node apps without a lot of the overhead typically associated with deploymnet. Although this episode will concentrate on heroku, I plan on covering other platforms in future episodes.
First, let’s look at the current stack of technologies used by activityOverlord.
I’m running OS X Mountain Lion locally on a MacBook Pro. I’ve got node installed and sails of course runs on top of that. Our models, currently the user model, uses a mongodb database also running locally on mountain lion. And finally, we store our sessions and sockets in memory.
To do this deployment the stack of technologies is going to change.
For example, instead of running locally on OS X Mountain Lion, node and sails will run on an instance of the hardware and software provided by heroku. Looking at the heroku docs, node.js runs on instances of Ubuntu 10.04.
Next, instead of our current local mongodb database, we’ll create a new instance of the database on a hosted solution provided by mongohq. Finally, we’ll move our in memory socket and session store to redis and specifically redis-to-go hosted by heroku as well.
Moving from local mongodb to hosted mongohq So you’ll first need to create an account on mongohq. Once you have an account and are logged in, create a new hosted mongodb instance using the sandbox option. Although this option isn’t for production it works for the purposes our project. Next, I created a user for the database that we’ll use to authenticate in our app.
So I want to incorporate this hosted database in my local instance of activityOverlord before we move it to heroku and we do that by changing the local.js
file. First let’s do a bit of review.
Our local configuration of mongoDB uses local.js
within the config folder while adapters.js
is completely commented out at the moment. The adapters.js
file is one place sails looks to for connections to databases. The local.js file overrides anything contained in adapters.js
. Also recall that local.js is part of .gitignore
so the file will not be saved to git or our repo.
The impact of this is that anything sails relies upon in terms of configuration in local.js will not be part of the deployment unless we provide for it somewhere else in the code or by using environment variables prior to deployment.
Before we address the deployment issues lets first go into the code and make a few changes to local.js
to use our new mongohq instance.
We no longer need the host, user, password, and database attributes because they are combined in a new attribute called url. I’ll copy and paste the url from the mongohq interface and then insert the username and password I created earlier for the database.
/config/local.js
1 2 3 4 5 6 7 8 9 10 |
|
Let’s see if this worked. I’ll go into the terminal and lift activityoverlord. Next, I’ll create a new user and great it looks like things are working. So, I’ll go back into mongohq and into the user collection and there’s my new user. To start things off, I’ll change my admin attribute to true. Now, I’ll log-in to the account and…great I have my admin user set-up.
Okay, now, let’s deploy to heroku. If you don’t already have an account on heroku, go ahead and create one now. Next, you’ll want to install the heroku toolbelt which can be found here. Finally, you’ll want to login to heroku from the terminal. So let’s go to the terminal and type heroku login, this is going to ask us for our heroku credentials and the first time you run it, it’s also going to set-up your public SSH key. Don’t worry If you don’t already have one, heroku will walk you through setting one up.
So now that we have an account, got the toolbelt installed, and we’ve logged in from the terminal, we want to back to the heroku dashboard and select create a new app. If you want to enter in an app name it will need to be something other than activityoverlord as app names must be unique. If you don’t put in an app name, heroku will create one for you and regardless, you can always go back and rename your app later.
Now you’ll recall that we have a local.js file pointing to the mongodb database hosted on mongohq. Since local.js
will not be part of our repo because of .gitignore
we need some way of letting the heroku server instance know about the mongodb configuration and we do this with environment variables. So let’s go back into our adapters.js
file and I’m going to copy and paste the mongodb configuration information from our local.js file into this adapters.js file. However, I’m going to replace where we have a string that contains the Username, password,path, and port of our mongodb instance on mongohq with an environment variable called DB_URL and that’s going to be prefaced by process.env.
/config/adapters.js
1 2 3 4 5 6 7 |
|
So, process.env is a node object that contains our environment which also includes the environment variables that we’ll add to it. So to add the environment variable to our heroku instance, let’s go back to the terminal and we’ll type heroku config:set DB_URL=
and to get our path I’ll look in our local.js
and copy the path of our mongohq instance and paste it here, I’ll also add —app activitityoverlord1 to point specify which app to associate with.
So we’ve set DB_URL on the remote heroku instance using heroku config:set
and used that environment variable in our adapters.js
file to point to our mongoHQ instance.
So how is heroku going to start ativityoverlord? We do that by creating a Procfile
in the root of our app. A Procfile is a mechanism for declaring what commands are run by our app’s dynos on the Heroku platform. More on dynos in a second. Let’s go back into the code and add a new file named Procfile with no extension. The file will have one line: web: node app.js
Next, make sure you have sails-mongo in your package.json
file and that it’s installed in node_modules. In fact it’s probably best to do an npm install to make sure you have all of the depencies installed. Now we need to link up the heroku end point with our project. Let’s go back to the heroku dashboard and look under the settings tab. Go ahead and c opy the git url and then go back to the terminal and enter: git remote add heroku <and then paste the git url here>
and press enter. Add all of your changes to git using: git add .
and then commit them using git commit -am “my very descriptive change log”
. Finally push the project by entering: git push heroku master
.
The last step before we fire up our browser and look at activityOverlord is to set up a dyno for our app. Heroku suggests thinking of a dyno asa virtualized Unix container. In sum, it’s the place where our app will run. To assign one dyno to our app, type: heroku ps:scale web=1
.
So let’s go back into the browser, refresh the app, and log-in. Everything looks to be working, however, open up the console and you’ll see an error. Socket.io is doing its job. Websockets isn’t working so its failing over to long polling so we still have a connection. As it turns out, heroku has just started to support web sockets and you must enable it on the application instance. To do that, we’ll go back to the console and type heroku labs:enable websockets
. It can take a second before websockets starts working. There we go. Also, I have had issues with it periodically failing and going back to web polling, but it is in beta so we’ll see how it improves over the coming weeks.
Next, I want to take a look at moving our session and socket store to redis. But first, why would we want to do this in the first place? Let’s take the following example. I have three instances of heroku running activityOverlord on each instance. I use a load balancer to distribute the incoming requests across the three instances.
Suppose we store sessions in memory, and on the first request the load balancer sends us to instance A where we authenticate and the session cookie is set for that server instance. On the next request we’re sent to instance B, where we haven’t yet authenticated and therefore won’t be able to access the resources we would have had access on instance A. Therefore we need some way for the multiple instances of our application to share the same session store.
This is why we’re moving our session and socket store to redis.
So let’s set-up redis. We’ll head back to the heroku dashboard and our activityoverlord instance. Select add-ons and redis-to-go. Under Plans, select Nano, or the free plan. I had to put in a credit card despite picking the free option. Select add nano for free. Go back to you instance of activityoverlord and select redis-to-go nano under add-ons.
Here you’ll see the configuration path to our redis instance. First, let’s go into activityoverlord and our session.js
file. As the documentation suggests, I’m going to uncomment, the adapter, host, port, db, and password keys. We can then go back to our redis-to-go configuration file and copy and paste each value into the keys in session.js
.
/config/session.js
1 2 3 4 5 6 7 |
|
Okay, let’s see if this worked. I’ll go back into the terminal and commit my changes and then push them to our heroku instance. Now let’s go back to the browser and try to log in. Even though that worked the true test, is whether our session id is in the redis database. To determine this, I’m going to use the redis-cli
command line tool. To use this tool we need to again use the host, port and password to authenticate to the database. Once connected I’ll use the keys
command passing in an *
as an argument to get all keys. And there’s our session key, great. The redis website has good documentation on other useful commands.
You might be asking yourself, I don’t really want to put my redis database credentials in my github repo, and you know you would be right, that would be a very bad idea! So instead we can use environment variables to set these credentials to our heroku instance. Let’s go back into session.js
and change the values for the host, port, db, and pass keys to environment variables.
/config/session.js
1 2 3 4 5 6 7 |
|
Now the server instance will be looking for those environment variables for the values. We’ll set them on the heroku instance the same way we did for DB_URL
using heroku config:set
.
Okay, now let’s do the same for sockets. We’ll go back to sockets.js
. Similar to session.js
we’ll uncomment the host, port, db, and pass keys and then insert the environment variables for the values.
/config/sockets.js
1 2 3 4 5 6 |
|
Now, I’m going to go back to the terminal and commit my changes again and push them to our heroku instance. Now I’ll go back to the browser, notice that I don’t have to login as my session is now maintained by redis whereas before we were doing things in memory which required us to login each time the server was lifted. I’ll manually log out and log back in. And great it looks like everything is working.
Okay, the last thing I want to address with deployment is changing the value of the NODE_ENV
variable from development to production. For sails one of the biggest outwardly facing changes as a result of using production instead of development is that all of our css files will be combined and minified into one file. Javascript files will also be combined and minified as well. In addition many modules utilize NODE_ENV
as a hook to determine whether to make changes based upon its value. We’re going to actually set the environment variable in our Procfile
. So let’s go over to the Procfile
and add web: NODE_ENV=production node app.js
. I’ll commit my changes and push them to heroku. Back in the browser I’ll refresh the page and then look at the source to confirm that all of my css is minified in one file and all of my javascript is minified in one file.
So now that we’ve successfully deployed activityOverlord to Heroku I want to address the work-flow for moving forward with development. The repo for activityOverlord will have the following set-up for local.js
, adapter.js
, session.js
, and sockets.js
. The local.js
file will default to our local instance of mongodb.
/config/local.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
|
If you want the local version of activityOverlord to use hosted mongohq instance, just uncomment and comment the following lines. Since local.js
will overwrite adapters.js
we can leave the existing code in it.
/config/adapters.js
1 2 3 4 5 6 7 8 9 10 |
|
Session.js will use the in memory session store, but when you want to deploy just uncomment these lines and comment your in memory config.
/config/session.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
|
The same holds true for the Sockets.js
configuration file.
/config/sockets.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
|
We’ve covered a bunch of material in this episode. I hope you found it helpful and as always thanks for watching.
]]>Howdy. Today I’m here to talk about my quest to understand the mysterious world of node module dependencies. The good news is, in the end, it’s really not very mysterious. Hopefully, by the end of this screencast I’ll have answered:
Although I’ll be talking about CommonJS mainly from a node perspective this is completely relevant to sails in that you’ll be using the module pattern in your sails development. If you’ve ever looked at the source of a node application you’ve come across some derivation of the following methods and objects.
Node applications are made up of modules, which are the equivalent of source text files. Actually that’s not completely accurate, a module can also be a compiled node module file, but I’m getting ahead of myself. Each module or text source file is its own domain and the objects, methods, variables, and classes contained in them are oblivious to other objects, methods, variables, and classes contained in other modules. They are in effect private to the module file that contains them. How do we make different module files aware of each other? That’s where the module pattern comes in and specifically node’s use of CommonJS.
The CommonJS implementation of the module pattern also protects the scope of your code to a particular module instead of opening up everything to a global scope. Therefore, only the modules that are connected in a way which I’m about to describe are aware of each other.
First a quick note on what CommonJS isn’t. At first I got confused between RequireJS and CommonJS. Although CommonJS does use a method named require, CommonJS and RequireJS are not the same thing.
My understanding is that RequireJS was created out of a need for a module loader within the browser as opposed to node which is running on the server. Anyway, don’t make the same mistake I did and start reading the RequireJS documentation thinking it’s the same thing as what’s used in node..
Now on to the module pattern. This pattern consists of two basic steps. One of the steps involves “requiring” a module you want to include from one file while from the other file or module “exposing” the stuff that you want to make available to other modules.
I’m going to talk about the “require” part of pattern first. I think the best way to learn this stuff is through examples. One of the early examples we’re exposed to in learning node is creating a server using the http module. For example:
1 2 3 4 5 6 |
|
I want to focus on the first line: var http= require(‘http’);
This line creates a reference or variable to the module http
. This happens to be one of the modules that comes compiled with node. Armed with our http variable we can now access everything that was exposed in that module via dot notation. For example, that’s how access the createServer
method.
However, let’s say we want to create our own module, how would we require it? I’m going to explore several ways of requiring a module but one initial way is to use:
var utility = require('./myModule.js');
So let’s create a new file called index.js.
index.js
1
|
|
…and I’ll paste our new require line into the file. Breaking this down we have an arbitrary var named utility referencing a javascript file myModule.JS located in the same directory or relative path as index.js. So before we starting using our new module, we need to create it. So, next I’m going to create another file called myModule.js in the same directory with the following code.
myModule.js
1 2 3 4 5 6 7 8 |
|
In this file I added a var called balance
and initialized it with a value of 100. Next I created a function addDeposit
. The last two lines are the important part. I exposed the var balance by exporting it via module.exports
. I could have named the attribute something other than balance but by convention I’m going to use the same name as the actual var. I did the same thing to expose the addDeposit function, that is using module.exports
.
Now let’s go back to our index.js file and we’ll use our newly created module.
index.js
1 2 3 4 |
|
I can access the exported var and function from myModule through dot notation and my reference named utility. In this case utility.balance
for the balance and utility.addDeposit(10)
for the function. Again the use of the name utiityl is completely arbitrary, I could have named this reference foo. So let’s see this in action.
I’ll go to the terminal and type node index.js to launch our module. And as we wanted the module returned the balance before and after the deposit.
Next, I’m going to expand this module to act like a class using a function as a constructor. Notice that in addition to the instance attributes and methods I also have class variables and methods (e.g. class_variable and class_method) that are not exposed via require.
myModule1.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
Keeping with the pattern after you expose or export the code you want to make available from your module, we’ll do the other step of the pattern by requiring the module. I’m creating a new file index1.js.
index1.js
1 2 3 4 5 6 7 |
|
Here I’m instantiating the Account class reference using the var account while passing in a new balance for the account as an argument. The other change is adding a deposit attribute to the account instance. Now, let’s see this in action.
I’ll go back to the terminal and type node index1.js to launch our module. And as we wanted the module returned the balance before and after the deposit as well as the amount of the deposit itself.
Next I want to show an example of the pattern where we can instantiate the Account class from within the require statement itself.
First, we’ll create a new module named myModule2.js.
myModule2.js
1 2 3 4 5 6 7 8 9 10 |
|
On line one I declare the var Account and do the export of the function in the same line of code. I capitalized the A in Account to signify that it represents a class, however, this isn’t necessary to making any of this work and is strictly a convention. I then build up an object that will eventually be returned when Account is instantiated. Now let’s create index2.js:
index2.js
1 2 3 4 5 6 7 |
|
Notice in line one where we’re doing our usual require, in this case by putting the arguments on the end of the statement we’re actually creating a new instance of the Account class assigning it to account while passing the argument with a value of 2000. Let’s see how this works.
The results this time reflect passing the initial balance in as an argument and we have access to our instance variables and method as expected.
The last example is where we export an object directly and I’m going to use our model in activityOverlord found in api/models/User.js
. Notice that we’re wrapping the entire file with module.exports.
myModule3.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
|
Next, we’ll require this module and take a look at a couple of its attributes.
index3.js
1 2 3 4 |
|
Let’s see it in action. As expected we get back the attributes we requested via dot notation.
So where can I put modules?
Okay so the last thing I want to cover is a more extensive look at where we can put modules and how to require them in these different locations. As I mentioned at the beginning of the screencast there’s a distinction between node’s core modules and our own custom file modules we’ve been working with. Core modules like http
can be referenced by using just the module name (e.g. ‘http’). For file modules we have a bunch of options.
So far, we’ve been accessing our modules via relative paths. But we could also use an absolute path like:
var myModule = require(‘/home/api/foo.js’);
You can also require a file module without using ‘/’, ‘./’ or ‘../’ however, the module must be placed in a node_modules folder. The node documentation has a good example I’ve included here.
So node will first look in the same directory where the file that it’s calling is located, in this example /home/ry/projects/node_modules
. If it doesn’t find it there it will traverse up the file system path until it either finds the node_modules folder and module or gets to the root of the path without finding it in which case returning a ‘module not found’ error.
The final area I want to look at is using Folders as Modules. This happens when you want to provide a package or library that is organized using a directory structure with one entry point to the package or library. For example, let’s say you have a new templating library superTemp and all of its supporting modules will be installed at the root of /superTemp.
You could require the library by using:
var superTemp = require(‘./superTemp’);
So what are my options for the entry point or the initial javascript file? If I create a file index.js and put it in ‘./superTemp’, node will run that file first. I could also use the extension index.node. Finally, if I wanted to use a different name than index you can create a package.json file using the following syntax:
1 2 |
|
So let’s see how that works. I’ve set-up an admittedly contrived directory structure under this example 1 folder. So we have a module index.js that requires another module, myModule.js in the otherDir folder. Notice that the parenthesis at the end of the statement which means we’ll be instantiating the function. myModule.js is a just a function that logs “Our app is launched!”. Finally, we have a file launcher.js that requires the folder /super_temp. So now I’ll go to the terminal and type node launcher and we get our log message that our app has launched.
Now let’s go into the example two folder. Again a contrived directory structure but here instead of an index.js file we have a package.json file. The package.json file points to myCrazyNamingConventionDealWithIt.js
module. And like our other example that module points to myModule in the otherDir folder. Let’s see what happens. And again we get the expected results.
Okay gang, I know that was a bunch of stuff. I hope it was helpful and thanks for watching as always.
Howdy and welcome back.
I want to clean-up a use case that will unfortunately crash the server. The situation occurs if a user creates an account and then while they are logged-in a different user with admin privileges deletes them from the database. When the deleted user attempts to sign out the following server error occurs: TypeError: Cannot read property ‘name’ of undefined
This is because publishUpdate tries to send the user.name attribute and the user instance no longer exists. This is an easy fix, let’s head over to the session controller. We can wrap the userUpdate(), userPublish(), req.session.destroy(), and res.redirect() methods in an if statement that checks whether a user exists. If the user doesn’t exist then we’ll just redirect to session/new via res.redirect(‘/session/new’);
If the user does exist we’ll let just pass through to our existing logic. So now when we try to do the same use case, the browser is redirected to session/new.
One other change I want to make is to prevent the socket from subscribing to the user model events unless the user is authenticated. To do this, we’ll modify the authenticated policy so that it looks for req.session.User, if it exists, the user is authenticated, and if not, we send a 403. We’ll then use that policy in policies.js within the config folder for the subscription action of the user controller. By doing this, the socket cannot subscribe to /user/subscribe unless the user is authenticated. Let’s check it out.
So now, when the user logs in, the non-authenticated socket does not respond to the event because they are not yet authenticated and therefore not subscribed. Once the other user logs in, however, they receive the original user’s logout event.
Thanks for watching.
]]>In this screencast, we’ll add a flash message that will let all users know when someone logs-in, logs-out, is added or deleted. So here, I’ve logged in on one browser normally and I’ll log in on another browser using icognito mode…and there’s our flash message. When I log out I see the flash message indicating the logout on the other browser. Now I’ll create a new user account and we see the corresponding flash message. Finally, I’ll delete the account and the flash message notifying us of the change is displayed..
So following our Real Time Model Events pattern from episodes 21 and 22, we’re already subscribed to the user class room and instance models. We did that on the server-side by creating a subscribe action to our user controller which does the actual room subscriptions. On the client-side within app.js we initiate the subscription by using: socket.get('/user/subscribe');
which, similar to an http get, hits our subscribe action.
So now all we have to do is use the publish methods to let the socket of any browser tab know that one of our events has occurred. First, let’s look at when the user logs-in. For that we’ll go into our session controller. We’re already using publishUpdate to let the socket know that someone has logged in, now we can expand this object to pass the user.name and an action attribute with a value of ‘ has logged in.’
On the client side, I’m going to open app.js and add to our popularly named cometMessageReceivedFromServer method to include an if statement that checks whether the message.verb is not equal to destroy and if it’s not call the displayFlashActivity method. This method, plays a sound and then through jquery displays the flash message.
In order to set this up we need to do a couple of quick things. First, I created a sounds folder which not surprisingly contain sound files. And then since I want this sound available on every page I put the following tags in the layout.ejs file.
So let’s see if this works. I’ll log-in and great, we get a slightly annoying sound and our flash message.
Back in the session controller’s destroy action, I’ll add an update when the user logs out. We expand the object again to include the user.name but this time an action attribute with the value of ‘ has logged out.’
Next, let’s open the User Controller’s create action and we’re already using publishCreate(user)
to update our User Administration page. So here I’ll just add the action attribute to the user object and we’ll be good to go.
Finally, let’s look at the destroy action of the user controller. Here we’re using the publishDestroy method to let our User Administration page know that a user was destroyed. The publishDestroy method only passes the id of the destroyed model, so I’m going to also use the publishUpdate method to pass the user.name and user.action to our client.
Now let’s see if this all works.
I have our two browsers open one in regular one in incognito mode. I’ve logged into one side and now I’ll log into the other…there’s our flash message. When I log out we see the flash message. When I add a user we get the appropriate flash message. Finally, when I delete the user account, we’re notified of the event.
Although this all works, I’d really prefer that you have to be logged in order to receive the flash messages. I’d also like the flexibility of making the activities persistent, that is saved to a database. So in the next screencast we’ll refactor this code to do just that.
Thanks for watching.
]]>Howdy and welcome to the second part of these screencasts dealing with real-time events. In this episode we’ll implement much of the client side portion of our code. As I said at the end of the last episode, you can implement your front-end, however, you’d like. I’m just learning the front-end so I wanted to start with server-side views and then refactor what I’ve done to include a framework like backbone to transition into a single page application. That, however, is for another day and here we’ll manipulate the DOM within our server-side views.
So let’s go back to the app.js file located in the assets/linker/js
directory and our event listener for the event message. As you may recall, within this file we’ve established a connection with the socket server, subscribed to our User model’s “class” room and “instance” room via socket.get()
. And we’re now awaiting message via socket.on('message', function(){})
.
Now in addition to logging this message I want to make changes to the DOM depending upon the contents of the message. To make the code cleaner, I’m going to define the callback function outside the event listener.
Once again I’m not the most imaginative person when it comes to function names and as proof of this I’m going to name the callback cometMessageReceivedFromServer
. This will be the method that will route our messages depending upon the model that is being updated. And we’ll define it down here at the bottom of the app.js file. I’m still going to log the message for debug purposes but now I’m going to check if the message is coming from the user model and if it is I’m going to call a new custom method called updateUserInDom and pass it the userId and the message.
updateUserInDom
is going to determine which page will ultimately receive the changesand route it to the appropriate method for that page. So in this case, the receiving page is /user
or the User Administration Page and since the action or verb is update, we’re going to route the message to the updateUser
method of an object called IndexPage. Remember these are all custom methods that I’m creating and have nothing to do with sails.
The updateUser
method is where we’ll update the DOM to display the icon-online.png or icon-offline.png
depending upon the value of loggedIn. If you take a look at the index.ejs file under the views/user
directory, I’m populating each row of DOM with two custom data attributes, data-id and data-model. These are then used as jquery criteria for identifying DOM elements. So we’ll find the row of the user id that we want to change and then replace the image src with the correct icon. Let’s see if it works
As you can see, the login status icon changes immediately when another user logs in or out.
What about when a new user is created? We want to update the User Administration Page with the new user or deleted user and their login status.
To do this we’ll use the publishCreate and publishDestroy
methods. So we head over to the create action of the user controller and insert the publishCreate
method passing the created user as an argument. Then we’ll look at the destroy action and insert our publishDestroy
method passing the id of the user that was destroyed. That’s it for the server-side. Back to the client and our app.js file. We’ll start at our old friend cometMessageReceivedFromServer
method. From there we’ll proceed to the updateUserInDom
method where the receiving page will again be /user
or the User Administration page. But here we’ll two cases to our switch statement. One for the create action and one for the destroy action. For the create action we’ll use the addUser
method. To clean things up and to show some functionality I’m going to use a template using the underscore library. So let’s take a look at the template, which I’ve stored in the linker/assets/templates
directory.
This template represents one row of the User Administration Page. Before using this template there were a few things I needed to set-up. The first was to add the underscore library to linker/assets/js
directory. Next, I made a slight change to the Gruntfile, changing the default template files to inject, from html to ejs. Finally, because we are using a hidden form for the delete button action, we need to have access to the csrf token. Since the template is added after the index.ejs page has been rendered on the server, we needed a way to get at the token.
There are several different ways this can be done (including make an ajax call to the server via a /csrfToken
route). However, I decided to make a change to the layout.ejs file appending the csrf token to my own namespaced attribute on the global window object. That way when the layout.ejs file is rendered, my object will be appended to the page.
So back in the app.js file I’ve built up an object called obj with the user plus the csrf token we obtained from the layout.ejs file.
Here, I’m using jquery to grab the last tr on the page and apply the template after that last tr tag, passing in the necessary data for the template in our obj object.
But what about when a user is deleted? That’s a lot easier. We’ll add the destroyUser
method and use jquery to find the appropriate user and remove that row from the DOM.
Woah…let’s see if all that stuff worked. I’ll restart the server and log myself in the first browser and create a user in the second browser and great…the user was added to the DOM with the correct login status. Now I’ll log out and login as another user, and this time delete the user we just created…and both sides updated like we wanted.
There’s one more thing to add. Each time the server comes up we want to make sure that the online attribute of each user is set to false. For that we can the sails bootstrap config file. Because I used an empty object as the first argument, all the users will be updated. The second argument is the attribute I want to update, in this case online is set to false. Finally I’ll trigger the callback back to the middleware stack. Now every time you start the server, users online attribute will be reset to false.
That was a long one. For those of you who made it through, I hope it was informative and as always thanks for watching.
]]>Before we get started, we need to update the current version of sails to 0.9.7. To do this, we’ll open our package.json file. In the sails dependency we’ll change the version from whatever version you currently have to ~0.9.7. Go into your terminal and in the root of our project directory type npm install and you’ll be ready to go.
So I’m really excited to get started with this part of activityOverlord. I’ll be taking us through, step-by-step, how sails makes use of web sockets to deliver real-time events in your application.
First, let’s take a look at what we’ll have accomplished by the end of this episode. So, I’m going to log-in…and we’re at our user administration page, and of course I show logged in by this check mark. In order to show this functionality I’m going to actually utilize two different browsers. One just a regular browser and another that’s in incognito mode. So now I’m going to log-in here as Nikola Tesla and I don’t know if you saw this on this side but Nikola Tesla’s status changed without me having to refresh the browser. So now focus your attention on this side of the screen and I’ll sign-out and you can see that old Nick’s status has been updated to offline. What about if we add a user, so now I’m going to sign up a new user and we’ll use our friend Aimee Mann and Aimee appears on our list as well as her login status. And will go ahead and sign her out. And then finally let’s go back in as Nikola Tesla and we’ll remove Amy from the list. Which in turn is removed from the other listening browser. But how does sails do that and how can we accomplish it. And that’s what we’re going to go into during this episode.
In Episode 18, I went over how to use plain jane node and socket.io. We learned that websockets are actually an extension of http. And since an http server can’t send data unless a client has requested it, web sockets allow for a server to send data unsolicited… You know what let me stop for a second. No one is dumb enough or brave enough to use these crazy Keynote transitions that I’m using. I found them so funny that I’ve decided to use them throughout this presentation. So I hope you take them in the spirit they are given and that they don’t distract you too much from the content. Anyway, back to programming.
So the http server can’t send data unless the client has requested it and websockets allow for a server to send data unsolicited once an initial connection is made. On the server side we built an http server with node, upgraded the http server to also act as a socket server and then on the client side we built up some html combined with javascript to connect to the server. Once the connection was made, any other client that connected would be eligible to send messages to the server which in turn emitted or broadcast those messages to the clients that were connected. We found out that part of a socket is just a unique id that identifies the socket or in our case the browser tab that is accessing the page.
Lastly, I showed how socket.io provides the concept of rooms that allow you to group sockets so that instead of messages going to all connected sockets, one can send messages only to those sockets that are associated with a room.
In Episode 20, I went over how sails can be used to emit messages based upon changes to a model using the rest blueprints. That episode may have confused some folks, but after the next several episodes I encourage you to go back to episode 20 and it should make more sense.
Similar to the way it automates many of the tasks associated with building an MVC framework, sails automates many of the tasks associated with enabling real time events within the MVC pattern.
I learn better by examples, so let’s look at one of the many ways sails automates enabling real-time functionality. I’ve named this functionality Real Time Model Events. And let me be clear here, Real Time Model Events is my term, it’s not something to do with javascript standards or anything the sail’s team uses, I just needed a way to identify some functionality and Real Time Model Events worked for me. I’d be curious, after you see this episode, whether the term was helpful or confusing to you.
Okay but what the heck are Real Time Model Events? Just like we create event handlers for DOM events using jquery, I’m going to show you a pattern to set-up event handlers for changes to models that get emitted to any socket that’s subscribed to the event.
Currently we have a User model in activityOverlord. One of the attributes of that model is a boolean called online. We want to know whenever the value of online changes so that we can provide a real-time update to the user administration page whenever a user logs in or out of the application. So where do we put our code?
If you’ve experimented with creating a project in sails, you’ve already been running a socket server and connecting to it when you hit the initial sails welcome page. Let’s check it out.
So I’m going to create a new sails project called socketExample. I’m going to start the sails’s server, and open up a page next to the terminal. Notice the handshake log message in the terminal. That’s our tab making a socket connection to the server. But where is that taking place? Let’s take a look at the code in the socketExample project. I want you to notice in the assets/linker/js directory we have a file added when we created this project called app.js. This app.js file is there to get you up and running with websockets. The structure of this file is purely optional but I’m going to use it as the base for the functionality in activityOverlord.
So here’s where we attempt to connect with the socket server. And then we listen for when this socket actually connects, and when it does, the socket starts listening for a message event from the server.
But wait, we didn’t create the socket server itself. That’s because sails does this for you automatically. You can have raw access to the socket.io server and client methods, but what I want to show you here is that a bunch of the work is done for you automatically if you choose to use it.
So, how do I register an event for our User model, online attribute, to listen for changes? We do that by using the following pattern.
The first step happens on the server-side within a controller. This involves subscribing the socket to the model “class” room and/or subscribing the socket to the model instance rooms. Remember, when I say socket, in this example, I’m talking about the page or browser tab that already connected to the socket server via var socket = io.connect(); back in our app.js file.
Now, You might be asking what’s the difference between subscribing to the model ‘class’ room versus the model ‘instance’ room? Subscribing to the model “class” room will enable the socket to listen for the creation of new model instances via a method called publishCreate(). Whereas subscribing to the instance class room enables the socket to listen for changes to existing models via the publishUpdate and/or publishDestroy methods. By the way, when you subscribe a socket to a model class room, that socket automatically gets introduced to the models instance room and will receive any changes via publishUpdate or publishDestroy.
So in activityOverlord we have a User model and we want to listen for the creation of new user’s as well as changes to existing users…so let’s leave all these cheesey transitions behind and go implement that in the code.
Here, we’re are in the user controller, and I’m going to add an action called subscribe.
Sails gives us access to the requesting socket via req.socket. So, first I’m going to subscribe to the User’s “class” room by entering User.subscribe and passing in req.socket as an argument. We’ve now subscribed to the User model class room.
Next, I’ll subscribe to the User model’s instance room by entering User.subscribe, again passing in the requesting socket via req.socket, but wait I need to pass a second argument, that being the existing instance user rooms or models. No problem, I’ll just wrap these two subscribe methods in a find query method. Now I can pass the returned users into the subscribe method as a second argument and we’ve now subscribed to the User model instance rooms.
But what about publishCreate, publishUpdate, and publishDestroy, where do we put those methods?
I’m going to come back to those methods in a minute, let’s move on in our pattern and address where we call our subscribe action. For that we’re going to move from the server back to our client. As you recall in app.js we’ve been notified by the socket server that a connection has been made and we’re now waiting on a message event. This is where we’re going to use a new method called socket.get() and we’re going to use it to “hit” our subscribe action.
You might be asking, I thought get was an http method, what does that have to do with sockets? Sails allows you to make requests like you would using http. You can not only make a get request over sockets, you can also use post, put, and destroy. I’m going to do a separate screencasts on using these methods but I wanted to mention that they were available.
So we’re back in app.js and here’s the code…that’s it, we’re now subscribed to both the class room and instance rooms of the User.model when each page is loaded.
So let’s review. We’ve connected to the socket server, we’ve subscribed to the User model class and instance rooms by calling the subscribe action of the UserController via socket.get and then within the subscribe action, subscribing to the user model by calling User.subscribe, and finally listening for message events from the socket server. We can now return to our controllers and start sending messages to our sockets based upon additions and changes to our model.
We started out this screencast with a goal of letting users know when other users login via the User Administration page. So let’s get to the session controller and do just that.
So here we are in the create action of the session controller. When a user authenticates, we’re going to let other users know by using the publishUpdate method. We’re going to pass the user id as the first argument, and then an object which will contain the changes that we want to send to other sockets. In this case whether loggedIn is true or false and the userid. Let’s try it out.
Okay, we have two browsers here, one in regular modeand one in incognito mode with the chrome console open in both tabs. I’m going to log into activityOverlord, which should trigger the publishUpdate and log it to the console. But the message event is not showing up in the console, what’s up? Well first let’s look at the terminal. Woah there’s a bunch going on here.
First, don’t be confused by the fact that the version of sails is v0.9.4 and that I’m getting these errors about installing sails locally. While creating these screencasts, I was using the development branch of sails in order to get the latest web socket functionality. You should be on 0.9.5 which encompassed the development branch when this screencast was made.
There are however some interesting errors here. Let’s take a look at this warning that we are trying to render a view (session/new) over sockets. If we look back at our subscribe action, we don’t have anything responding to our action. Which is an important reminder that we should at least send back a 200 if the action is successful. I’ll do that now. But why are we getting a warning that we’re trying to render a view? Ahh, let’s take a look at our policies. The current policy that will affect our subscribe action is admin. And when you take a look at that policy, it requires that we be authenticated before gaining access to the action. And look what happens if we’re not authenticated. We’re redirected to session/new which actually does render a view.
This is simple enough to fix. We’ll just add subscribe to our flash policy. Let’s see what happens.
I’ll reload the sails server, login in, and there’s our message in the console. And if we look at it, the message contains the user.id and that loggedin is true…perfect. Now you may ask why didn’t the browser that I logged in on also have a console message? It actually did, however, after I logged in successfully, the browser was redirected to the User Administration page which was refreshed instantly making it difficult to see the message before the browser refreshed.
What about when the user logs out? We’ll add a similar publishUpdate method within the delete action of the SessionController. Instead of setting loggedIn as true will set it to false. So after restarting the sails server, we’ll log out which produces a new message that has the loggedIn attribute set to false.
This is kind of the half-way point of this screencast. In the next episode I’ll go over one way of handling the DOM manipulation on the client based upon the events we’re receiving from our socket. See ya at the next screencast and thanks a lot for watching.
]]>