Chapter 4. Using the blueprint API

This chapter covers

  • Expanding your understanding of the relationships among requests, routes, and actions in a Sails API
  • Generating a backend API with blueprint routes and blueprint actions
  • Integrating frontend requests with the blueprint API
  • Using blueprint shortcut routes to access data from the browser
  • Upgrading with WebSockets to provide realtime features

Chad loved Brushfire’s ability to display and add YouTube videos. He soon discovered, however, the limitations of the simulated backend. When he refreshed his browser, all of the videos he added were no longer listed! We explained that this was because the links to the YouTube videos he added were stored temporarily in an array in memory. When he refreshed the browser, the array returned to its original state without his additional links in the list. Chad wasn’t happy about this. Fortunately, we had anticipated this problem and had begun work on a true backend API that would store the added video links in a database. The next time Chad added a hundred videos to Brushfire (oops!), they’d still be there when he refreshed his browser. Let’s see how we added this functionality to Brushfire.

In chapter 3, we created our initial frontend using Google’s Angular web framework. You learned how to deliver static assets using the Sails asset pipeline. Finally, we prototyped a backend using the (previously mentioned) array to simulate a model and database response. We’re now ready to refocus our efforts from the frontend to the backend and replace the fake or simulated backend with a fully operational API. To do this, we’ll use another core component of Sails called blueprints. Blueprint routes and actions, combined with model methods, provide automatic CRUD endpoints around a given resource.

Definition

Recall that CRUD stands for create, read, update, and delete. These are the most common operations performed on stored data in an application.

This means is that, with a single command from the terminal window that generates a Sails API, you can start making AJAX requests to CRUD endpoints. These endpoints can create, update, and/or delete data in a model, as well as retrieve data from a model. It’s so easy to create a JSON CRUD API around a resource that you don’t even need to simulate a response from the backend. Instead, you can now instantly create the real thing.

4.1. Prototyping with blueprints

Figure 4.1 illustrates what you know about a backend API so far.

Figure 4.1. The backend API endpoint is triggered by a request from the frontend . The Sails router attempts to match the route , and if it finds a match, executes an action . The action does stuff and responds to the frontend .

Routes are triggered by requests. When a request is made, the Sails router looks for a matching route. If it finds one, the router executes a JavaScript function called an action. The action fulfills the requirements of the request by doing stuff added to the action and responds to the device that made the original request. The request, route, router, and action form the building blocks of the API. Instead of manually creating the routes and actions necessary for common CRUD tasks around a resource, you can generate them automatically using blueprints. But enough theory, let’s get started building your API.

4.1.1. Designing an API around a user interface

In chapter 3, you identified two frontend requests on the video page of your interactive mockup: a request to list videos and a request to add videos, as shown in figure 4.2. You transformed this interactive mockup into the markup in assets/videos/index.html, and the requests were fulfilled by simulated responses. In this chapter, you’ll fulfill those requests with a real backend based on blueprints.

Figure 4.2. The video page can send two requests: one to post new video submissions and the other to get the initial videos of the page.

In chapter 1, you learned the importance of also documenting the requirements of each request. The requirements for your requests can be found as a link in the chapter 4 hub, located at http://sailsinaction.github.io/chapter-4/. The requirement of the list videos action is that Sails find all video records in the database and then respond with a JSON-formatted list of those videos. The requirement of the add videos action is that Sails take two parameters—title and src—as the basis of new records in the video model. Because these two requests are related, you’ll group their routes together in one API resource named /video.

4.1.2. Obtaining the example materials for this chapter

To speed up your progress, you’ll begin the next section with a fully baked frontend. We’ve added a few necessary changes to the frontend that you started back in chapter 3. These changes allow you to access your soon-to-be-created backend JSON CRUD API. Clone the chapter 4 repository https://github.com/sailsinaction/brushfire-ch4-start and install its Node module dependencies via npm install.

4.1.3. Generating an API in Sails

Generating a JSON CRUD API in Sails is easy. Head over to the terminal window and type

~/brushfire $ sails generate api video
info: Created a new api!

That’s it. You now have a video JSON CRUD API.

Note

Recall that you already used the generate command in chapter 3 when you replaced the default server-rendered homepage with a static home-page. In this case, you ran this command to generate the necessary files and folders for a working API.

What did you create? Opening the project in Sublime reveals two new files in the brushfire/api/ folder: /controllers/VideoController.js and /models/Video.js. But the following two listings reveal that both the controller and model files are empty.

Example 4.1. The initial contents of VideoController.js
module.exports = {

};
Example 4.2. The initial contents of Video.js
module.exports = {

};

Unless disabled, Sails uses the mere existence of these two files to build the blueprint routes and actions in table 4.1 each time the Sails server is started via sails lift. Therefore, unlike explicit routes, which are created and configured in brushfire/config/routes.js, blueprint shadow routes are created within the Sails core.

Table 4.1. An overview of the blueprint API generated for the video resource

CRUD operation

Shortcut blueprint routes

RESTful blueprint routes

Route address

Target

Route address

Target

Verb

Path

Blueprint action

Verb

Path

Blueprint action

Read GET /video/find find GET /video find
Read GET /video/find/:id find GET /video/:id find
Create GET /video/create create POST /video create
Update GET /video/update/:id update PUT /video/:id update
Delete GET /video/destroy/:id destroy DELETE /video/:id destroy
Definition

The Sails core, in the context of this section, means the source code of the Sails module. Recall that each Sails project has the sails module installed in brushfire/node_modules/sails.

You could override blueprint routes or actions by creating an explicit route or action with the same name, but for now you’ll use the blueprint API as is.

4.1.4. First look at Sails auto-migrations

Sails created a Video model when it generated the video CRUD API, so it will prompt you for some information about model migrations when you next start the server. Go back to the terminal window and start Sails and your application from the command line by typing

~ $ sails lift
info: Starting app...

After a few seconds, you should see terminal output similar to figure 4.3.

Figure 4.3. Choosing between safe, alter, or drop auto-migration in Sails

Let’s talk about data auto-migrations, which are quite fascinating. You’ll select the alter option, and later in chapter 6 we’ll come back to the topic. Also, in lieu of being prompted with this dialog each time you start Sails, open brushfire/config/models.js in Sublime and uncomment the migrate parameter, which has a default value of alter. When Sails is restarted using sails lift, auto-migrations will be set to alter automatically.

4.2. Shortcut blueprint routes

Shortcut blueprint routes allow you to interact directly with the underlying database via the browser’s URL bar. Why would you want to do that? Exploring the contents of one or more databases as you develop an API usually requires switching between different database viewers. Having a quick way to access information from a browser across multiple databases can greatly improve the speed of your workflow. When you generated the JSON CRUD API for the video resource, five shortcut blueprint routes were also exposed and made available. These routes, which correspond to common CRUD operations, enable you to find, create, update, and delete/destroy records from the browser. A list of the shortcut blueprint routes is shown in table 4.2.

Table 4.2. Blueprint shortcuts routes and blueprint actions

Route address

Target

Verb

Path

Blueprint action

GET /video/find find
GET /video/find/:id find
GET /video/create create
GET /video/update/:id update
GET /video/destroy/:id destroy

We pointed out in chapter 1 that the first thing you might notice about shortcut blueprints are that the route address uses the same verb, GET, for each route. Wouldn’t that violate the convention that UPDATE, POST, and DELETE should produce no side effects? Yes, it would violate the convention, which is why you should never use shortcut blueprint routes in a production application. But during development, they’re insanely useful. Later, when you transition to production, we’ll show that they’re very easy to disable.

4.2.1. Creating records with blueprint shortcut routes

Your current task is perfectly suited to shortcut blueprint routes. Recall from chapter 3 that the videos array contained three records with two attributes: the title of the YouTube video and the src URL of the video. You want to start development with some initial YouTube video records in the video model. Shortcut blueprint routes make it easy to re-create the catchy yet annoying “GANGNAM STYLE” YouTube video as a record in the video model. Make sure Sails is running via sails lift and navigate your browser to http://localhost:1337/video/create?title=FUNNY BABY VIDEOS &src=https://www.youtube.com/embed/_FvTVWjLiHM. Your browser should display a video record similar to figure 4.4.

Figure 4.4. The shortcut blueprint route triggered the create blueprint action, which created a new video record in the video model and responded with the record’s contents as JSON.

Note

If you’re a JSON geek, like us, you may notice in figure 4.4 that there aren’t any quotes on the keys returned in the browser. That’s because we’re using a Chrome extension that uses JSON.parse to beautify the JSON.

How was this video record created? Let’s go through the steps executed by this route illustrated in figure 4.5.

Figure 4.5. The Sails router listens for an incoming request B and tries to match it to an explicit route. Not finding a match with an explicit route, it next tries to match it to a shortcut blueprint route c. In this example it finds a match, executes the create blueprint action d, and e responds with a 200 status code and the newly created video record as JSON.

The GET request to /video/create matched a shortcut blueprint route, 'GET /video/create' . The path also contained a query string. Recall from chapter 1 that a query string is part of the route address, as illustrated in figure 4.6.

Figure 4.6. The start of the query string uses a question mark (?) followed by field/value pairs where each pair is separated by an equals sign (=) and the field/value pairs are separated by an ampersand (&).

The matched route triggered the create blueprint action , which parsed the title and src parameters, created the record, and responded with JSON and status code 200 . With Sails you can create, find, update, and delete records directly from your browser’s URL bar.

Using the same steps, now create two additional records to the video model for the “Justin Bieber” and “Charlie bit my finger” YouTube videos. You can copy and paste the following URLs into your browser’s URL bar:

http://localhost:1337/video/create?title=Justin Bieber – Baby ft. Ludacris&src=https://www.youtube.com/embed/kffacxfA7G4

http://localhost:1337/video/create?title=Charlie bit my finger – again !&src=https://www.youtube.com/embed/_OBlgSz8sSM

Let’s expand our exploration of shortcut blueprint routes to include routes to find, update, and delete video records.

4.2.2. Accessing the database from the URL bar

We’ll first examine how to find records with shortcut blueprint routes. To obtain a list of all video records, make sure Sails is running using sails lift, and navigate your browser to localhost:1337/video/find. Your browser should look similar to the browser in figure 4.7.

Figure 4.7. The shortcut blueprint route uses the find blueprint action and responds with a list of records from the video model.

The path /video/find gets all of the video records. If you want to find a particular record, you’ll use the :id parameter at the end of the path: /video/find/:id. In figure 4.7, notice how each video record has a unique id. This id is typically assigned by the database when the record is created. Let’s use the shortcut blueprint route to find a particular video record. Open your browser and navigate to http://localhost:1337/video/find/3. Your browser should look similar to figure 4.8.

Figure 4.8. When you add an :id parameter to the URL, in this case /3, the response is a single record from the video model.

Here, the route returned the record with the id of 3 to the browser. Without the :id parameter, the find blueprint would have returned all video records.

Next, let’s update the title parameter of a video record. You can do that by passing in the id of the record you want to update. The id becomes the find criteria.

Definition

Criteria is simply a dictionary that Sails uses to select one or more records from the database.

The find criteria is followed by the parameter you want to update in the query string, in this case the title. Navigate your browser to http://localhost:1337/video/update/3?title=Charlie bit my finger – again and it HURT!!. Your browser should return the updated video record with the new title, as in figure 4.9.

Figure 4.9. The shortcut blueprint route uses the update blueprint action and responds with the record containing the updated title.

By using the path video/update/3 followed by the new title as a query string, the route updated the video record. Finally, let’s delete a record using a shortcut blueprint route with the destroy blueprint action. Navigate your browser to localhost:1337/video/destroy/3. Your browser should look similar to figure 4.10.

Figure 4.10. The shortcut blueprint route uses the destroy blueprint action and responds with the deleted video record.

You pass in the id of the record you want to delete, and the destroy blueprint action returns the destroyed video record. The shortcut blueprint routes are a useful tool in your development workflow, and their utility will become more and more apparent throughout the book.

4.3. Connecting the frontend to your new API

Now that you have a JSON CRUD API around the video resource, you can connect the frontend to it and satisfy the required functionality of your page’s requests. Table 4.3 illustrates the RESTful blueprint routes that are exposed when the Sails server starts.

Table 4.3. RESTful blueprint routes and blueprint actions

Route address

Target

Verb Path Blueprint action
GET /video/ find
GET /video/:id find
POST /video create
PUT /video/:id update
DELETE /video/:id destroy

In chapter 3, you simulated a backend request via a combination of the $timeout method and the use of an array. You’ll now replace that code with the Angular $http service to make requests to your API. Specifically, you’ll use $http.get() and $http.post() to make AJAX requests to find and create records in the video model. You could just as easily use jQuery, but you’ll use Angular for consistency.

4.3.1. Finding records with AJAX

When the videos page initially loads, it expects to receive all records from the video model as an array. You can obtain the video records for that array via REST blueprints and the find blueprint action. But first take a look at the Angular AJAX GET request in brushfire/assets/js/video-page.js.

Example 4.3. The Angular AJAX GET request to /video

After making the request, the find blueprint action returns all the records in the video model as dictionaries in an array. Figure 4.11 shows how this process works within the API.

Figure 4.11. The steps in a RESTful blueprint route include recognition of the incoming request by the Sails router , matching the request to a route , and executing the find blueprint action , which queries all video records and responds with an array of video record dictionaries.

The GET request to /video matched a RESTful blueprint route 'GET /video'. This triggered the find blueprint action, which queried all the records of the video model and responded with them in an array of JSON dictionaries. The important distinction here is that you’re now retrieving the videos from an actual backend API instead of a static array.

4.3.2. Creating a record with AJAX

Now you need to send the Add Videos request when the Submit Videos button is clicked. You’ll use a blueprint API again this time to create a new video record. Take a look at the Angular AJAX POST request in brushfire/assets/js/video-page.js in the next listing.

Example 4.4. The Angular AJAX POST request to /video

Recall that in chapter 3 you wired up the Submit button of your form to push the new video onto an array on the $scope. You’ve now refactored the code to trigger an Angular AJAX POST request to /video instead. Let’s see this in action. Make sure Sails is running via sails lift and navigate your browser to http://localhost:1337/videos/, which should look similar to figure 4.12.

Figure 4.12. The page triggers the RESTful blueprint route and the find blueprint action and then responds with all records in the video model.

Fill out the form and add a new record with the parameters in table 4.4.

Table 4.4. The parameters to create a new YouTube video record

Parameter

Value

title The North Face: Alex Honnold in Yosemite
src https://www.youtube.com/watch?vICBrXUuwvgg

Your browser should now look similar to figure 4.13.

Figure 4.13. The RESTful route and a create action were triggered by the Angular AJAX POST request, and the action responded with the newly created record of the video.

You can see how this process works within the API in figure 4.14.

Figure 4.14. The RESTful blueprint route and a create action created a new video record based on a POST request to /video.

The POST request to /video matched a RESTful blueprint route, 'POST /video' . This triggered the create blueprint action , which parsed the title and src parameters, created the record, and responded with JSON and status code 200 . Once again, you’re creating a video from an actual backend API instead of an array. Next, let’s explore the remaining RESTful blueprint routes: find, update, and destroy.

4.4. Exploring the REST of the blueprint API

Let’s take the RESTful blueprint find, update, and destroy routes for a quick spin. Oftentimes, you’ll want to test a route, but because of the complexity of the query parameters, using blueprint shortcut routes can become problematic. Enter Postman, the indispensable way to access and test an API. You installed Postman in chapter 1.

4.4.1. Locating a particular record with AJAX

Two RESTful blueprint routes use the find action. RESTful blueprint routes differentiate between obtaining a list of all records in a model and a single record in a model by using an :id parameter at the end of the path. If you make a GET request to /video/:id where the :id is the id of the record you want to find, Sails will return a single record. Let’s see this in action. From within Postman make a GET request to /video/2, similar to figure 4.15.

Figure 4.15. Here, you use Postman to make a GET request to the /video/2 path . The API responds with a JSON dictionary of the particular video record .

After making the request, Sails returns the record with an id of 2 as a JSON dictionary in Postman.

4.4.2. Updating a record with AJAX

Suppose you’re not satisfied with the title of a YouTube video. You want to change the title of Justin Bieber - Baby ft. Ludacris to just Ludacris.

Figure 4.16 demonstrates how to make the PUT request in Postman.

Figure 4.16. Here, you use Postman to make a PUT request to the path /video/2 with the URL parameter title=Ludacris . The API responds with a JSON dictionary of the updated video record .

Postman made the request on your behalf, updated the record, and returned the updated video record as JSON dictionary.

4.4.3. Deleting a record with AJAX

The Justin Bieber video has over a billion views and we’re probably responsible for at least a thousand. Enough! We’ll now show you how to delete the Justin Bieber record from the video model and in the process demonstrate another useful endpoint in the blueprint API. Figure 4.17 demonstrates how to delete a record by making a request in Postman.

Figure 4.17. Here, you use Postman to make a a DELETE request to /video/2 that contains the record id that you want to delete. The API responds with the JSON video record that was deleted .

Postman made the request, deleted the record, and returned the deleted record as a JSON dictionary. We’ve now reviewed shortcut blueprint routes, RESTful blueprint routes, and blueprint actions. You’ll get your first taste of realtime programming in the next section using WebSockets.

4.5. Upgrading to WebSockets

After showing Chad the latest version of Brushfire using our prototyped API, he told us that he wants Brushfire to be realtime. We weren’t exactly sure what he meant by this. After a few minutes of probing, we deduced that Chad wants Brushfire to update all browsers currently connected to the site at a particular moment with new YouTube videos as they’re added. Luckily, we can quickly accomplish this by replacing our Angular AJAX methods with Sails WebSockets methods.

Chapter 14 explains in detail how to configure and use WebSockets in Sails. The Hypertext Transfer Protocol requires that a user-agent make a request to the server before the server can send a response to the client. WebSockets upgrades HTTP so that, once a connection is established, a server can send messages and data to the client at will. This mechanism is what Chad refers to as realtime. Let’s take our first look at how WebSockets and realtime programming work.

4.5.1. Replacing $http.get() with io.socket.get()

Chad wants some way to update all browsers currently on the Brushfire website without a manual page refresh and after another user creates a new YouTube video link. You’ll use WebSockets with some Sails components, pictured in figure 4.18, on the frontend and backend to satisfy this requirement.

Figure 4.18. Replacing the AJAX GET request with the Sails WebSockets GET enhances the functionality of the find action.

Every new Sails application has a sails.io.js file, located in the brushfire/assets/js/dependencies folder. By adding this file to the page in a script tag, Sails automatically connects the browser to the Sails WebSockets server .

You can also equate this file to the way adding jQuery gives you access to jQuery AJAX methods. Sails provides similar AJAX-like methods to make requests, but instead of using HTTP, Sails uses WebSockets. Table 4.5 compares Angular AJAX methods with their Sails WebSockets equivalents.

Table 4.5. Angular AJAX requests vs. Sails socket requests

Angular AJAX requests

Sails socket requests

$http.get() io.socket.get()
$http.post() io.socket.post()
$http.put() io.socket.put()
$http.delete() io.socket.delete()

You’ll use io.socket.get() instead of $http.get() to make the GET request to /video. The Sails router will again listen for an incoming request and match the request with the blueprint RESTful find route that triggers the blueprint find action. The blueprint find action then tries to find all the records of the video model. What’s new is that the blueprint find action has some additional functionality when triggered with Sails’ WebSockets GET method. The action is now aware that this is a socket request and automatically places the requesting browser on a list to be notified if there are any changes to the video records found by the action. The blueprint find action places the requesting browser on another list to be notified if any new records are created in the video model. Finally, the blueprint find action responds with an array of the found video records as an array of JSON dictionaries. Let’s see this in action by replacing the Angular AJAX GET request with the Sails socket equivalent. Open brushfire/assets/js/video-page.js in Sublime and make the changes outlined here.

Example 4.5. Replacing Angular AJAX GET request with io.socket.get

The io.socket.get() method requires a URL path as a first argument, an optional data dictionary, and a callback. The callback will have two arguments, a resData dictionary that contains the body of the response and a JSON WebSockets response (jwres) that contains the entire response, including headers, body, and status code.

You might wonder why you add the io.socket.on() method . Earlier, we explained that when io.socket.get('/video') triggered the blueprint find action, that action added the requesting browser’s socket to a list. Whenever a video record is changed or a new video record is created, that list is notified of the event. The io.socket.on() method listens for the event and, if triggered, updates the video array on $scope . Because Sails socket requests are not part of Angular, you need to use $scope.apply() in order for the digest cycle to kick in and update the DOM. You’re now equipped to listen for changes to the video model and update the DOM accordingly. To complete the process, you need to update the Add Video request to Sails WebSockets.

4.5.2. Replacing $http.post() with io.socket.post()

You now want to replace the Angular AJAX POST method with a method that uses WebSockets. The goal is that when a user submits a new YouTube video to Brushfire, an event will be triggered that lets any other user on the video page know that a new video was added. It will update the videos list with the new video without users having to refresh their browsers. Let’s replace the Angular AJAX POST request with the Sails WebSockets equivalent. Open brushfire/assets/js/video-page.js in Sublime and make the changes outlined in the following listing.

Example 4.6. Replacing Angular AJAX POST request with io.socket.post

Let’s look at this in action. Make sure Sails is running via sails lift from the command line. Open a browser and navigate to localhost:1337/videos. Open a second browser window and navigate to localhost:1337/videos. This will simulate two different users accessing Brushfire. From either window, create a new YouTube video with the parameters in table 4.6.

Table 4.6. The parameters of a new YouTube video record

Parameter

Value

Title The North Face: Alex Honnold in Yosemite
YouTube URL https://www.youtube.com/watch?v=ICBrXUuwvgg

Your browser should look like figure 4.19.

Figure 4.19. When the io.socket.post() method made a POST request to /video, the create action added the new video record that emitted a video event. This event was received by the previously added event handler, which triggered an update to the DOM with the new video record.

The other window should automatically update with the new YouTube video.

4.6. Summary

  • Sails blueprints automatically generate the routes and actions necessary to produce JSON CRUD APIs around a resource.
  • You can use blueprint shortcuts routes to find, add, update, and delete video records.
  • Sails’ WebSockets support allows the backend to send events to frontend user-agents at will.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset