sailsCasts

Learning about sails.js one screencast at a time.

Setting Up the Development Environment and Creating Your First Project

| Comments

Follow me on twitter here.

Subscribe to the sailscasts mailing list here.

Transcript

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.

Let’s Start a New Adventure

| Comments

Follow me on twitter here.

Subscribe to the sailscasts mailing list here.

Transcript

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.

sailsCasts Answers: Ep8 - How Do Blueprint: Actions and Blueprint: Routes Work in Sails?

| Comments

The repo for this episode can be found here: https://github.com/irlnathan/sails-crud-api-sailsAnswers8

Transcript

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.

sailsCasts Answers: Ep7: How Do I Create a Restful Json CRUD Api in Sails From Scratch?

| Comments

The repo for this project can be found here: https://github.com/irlnathan/sails-crud-api

Transcript

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.

  • So the find request will use the http verb get with the path /sleep/:id? and bind to the sleep controller and find action.
  • The create request will use the verb post with the path /sleep and bind to the sleep controller and the create action.
  • The update request will use the verb put with the path /sleep/:id? and bind to the sleep controller and the update action.
  • and finally, the delete request will use the verb 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
// a CREATE action  
create: function(req, res, next) {

    var params = req.params.all();

    Sleep.create(params, function(err, sleep) {

        if (err) return next(err);

        res.status(201);

        res.json(sleep);

    });
}

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
module.exports.routes = {

  '/': {
    view: 'home/index'
  },

  // Custom CRUD Rest Routes
  'post /sleep': 'SleepController.create'

};

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
{
    "hours_slept": "8",
    "sleep_quality": "good",
    "createdAt": "2013-12-10T21:31:00.442Z",
    "updatedAt": "2013-12-10T21:31:00.442Z",
    "id": 1
}

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
   // a FIND action
find: function (req, res, next) {

  var id = req.param('id');

  var idShortCut = isShortcut(id);

  if (idShortCut === true) {
      return next();
  }

  if (id) {

      Sleep.findOne(id, function(err, sleep) {

          if(sleep === undefined) return res.notFound();

          if (err) return next(err);

          res.json(sleep);

      });

  } else {

      var where = req.param('where');

      if (_.isString(where)) {
              where = JSON.parse(where);
      }

      // This allows you to put something like id=2 to work.
      // if (!where) {

      //     // Build monolithic parameter object
   //    params = req.params.all();

   //    params = _.omit(params, function (param, key) {

   //        return key === 'limit' || key === 'skip' || key === 'sort'

   //    });

      //   where = params;

      //   console.log("making it here!");

      // }

      var options = {
                  limit: req.param('limit') || undefined,
                  skip: req.param('skip')  || undefined,
                  sort: req.param('sort') || undefined,
                  where: where || undefined
          };

          console.log("This is the options", options);
              
      Sleep.find(options, function(err, sleep) {

          if(sleep === undefined) return res.notFound();

          if (err) return next(err);

          res.json(sleep);

      });

  }

  function isShortcut(id) {
      if (id === 'find'   ||  id === 'update' ||  id === 'create' ||  id === 'destroy') {
      return true;
      }
  }

},

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 404not 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 404not 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
[
    {
        "hours_slept": "8",
        "sleep_quality": "good",
        "createdAt": "2014-01-09T23:36:01.552Z",
        "updatedAt": "2014-01-09T23:36:01.552Z",
        "id": 1
    },
    {
        "hours_slept": "12",
        "sleep_quality": "great",
        "createdAt": "2014-01-11T05:08:52.398Z",
        "updatedAt": "2014-01-11T05:08:52.399Z",
        "id": 2
    },
    {
        "hours_slept": "4",
        "sleep_quality": "poor",
        "createdAt": "2014-01-11T05:09:10.319Z",
        "updatedAt": "2014-01-11T05:09:10.319Z",
        "id": 3
    },
    {
        "hours_slept": "6",
        "sleep_quality": "so-so",
        "createdAt": "2014-01-11T05:09:20.456Z",
        "updatedAt": "2014-01-11T05:09:20.456Z",
        "id": 4
    },
    {
        "hours_slept": "10",
        "sleep_quality": "good",
        "createdAt": "2014-01-11T05:09:30.885Z",
        "updatedAt": "2014-01-11T05:09:30.885Z",
        "id": 5
    }
]

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
{
    "hours_slept": "12",
    "sleep_quality": "great",
    "createdAt": "2014-01-11T05:08:52.398Z",
    "updatedAt": "2014-01-11T05:08:52.399Z",
    "id": 2
}

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
localhost:1337/sleep?where={
    "id": {
        ">":  1}
}

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
localhost:1337/sleep?where={
    "id": {
        "!":  4}
}&limit=3&sort=id desc

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
[
    {
        "hours_slept": "10",
        "sleep_quality": "good",
        "createdAt": "2014-01-11T05:09:30.885Z",
        "updatedAt": "2014-01-11T05:09:30.885Z",
        "id": 5
    },
    {
        "hours_slept": "4",
        "sleep_quality": "poor",
        "createdAt": "2014-01-11T05:09:10.319Z",
        "updatedAt": "2014-01-11T05:09:10.319Z",
        "id": 3
    },
    {
        "hours_slept": "12",
        "sleep_quality": "great",
        "createdAt": "2014-01-11T05:08:52.398Z",
        "updatedAt": "2014-01-11T05:08:52.399Z",
        "id": 2
    }
]

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
// an UPDATE action
    update: function (req, res, next) {

        var criteria = {};

        criteria = _.merge({}, req.params.all(), req.body);

        var id = req.param('id');

        if (!id) {
            return res.badRequest('No id provided.');
        }

        Sleep.update(id, criteria, function (err, sleep) {

            if(sleep.length === 0) return res.notFound();

            if (err) return next(err);

            res.json(sleep);

        });
    },

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
http://localhost:1337/sleep/3
{
  "added_3": 42
}

…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
  // Custom Action Route
  'get /sleep/new': 'SleepController.new',

  // Custom CRUD Rest Routes
  'get /sleep/:id?': 'SleepController.find',
  'post /sleep': 'SleepController.create',
  'put /sleep/:id?': 'SleepController.update',
  'delete /sleep/:id?': 'SleepController.destroy',

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
// a DESTROY action
    destroy: function (req, res, next) {

        var id = req.param('id');

        if (!id) {
            return res.badRequest('No id provided.');
        }

        Sleep.findOne(id).done(function(err, result) {
            if (err) return res.serverError(err);

            if (!result) return res.notFound();

            Sleep.destroy(id, function (err) {

                if (err) return next (err);

                return res.json(result);
            });

        });
    },

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 404not 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.

sailsCasts Answers: Ep6 - How Does the Http Request/response Protocol Work With Routes

| Comments

Transcript

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
[
    {
        "hours_slept": "12",
        "sleep_quality": "great",
        "createdAt": "2014-01-03T00:29:55.921Z",
        "updatedAt": "2014-01-03T00:29:55.921Z",
        "id": 1
    },
    {
        "hours_slept": "8",
        "sleep_quality": "good",
        "createdAt": "2014-01-03T00:30:03.544Z",
        "updatedAt": "2014-01-03T00:30:03.544Z",
        "id": 2
    },
    {
        "hours_slept": "6",
        "sleep_quality": "so-so",
        "createdAt": "2014-01-03T00:30:13.998Z",
        "updatedAt": "2014-01-03T00:30:13.998Z",
        "id": 3
    },
    {
        "hours_slept": "5",
        "sleep_quality": "bad",
        "createdAt": "2014-01-03T00:30:21.294Z",
        "updatedAt": "2014-01-03T00:30:21.294Z",
        "id": 4
    },
    {
        "hours_slept": "3",
        "sleep_quality": "bad",
        "createdAt": "2014-01-03T00:30:25.998Z",
        "updatedAt": "2014-01-03T00:30:25.998Z",
        "id": 5
    }
]

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.