sailsCasts

Learning about sails.js one screencast at a time.

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.

Comments