Ted Neward | Updated July 12, 2016 - Published November 3, 2015
Ever find yourself wandering in the cheese aisle of a gourmet shop, overwhelmed by the impressive display of cheeses from all over the world? It’s a nice problem to have, but the trouble is in deciding which of them to bring home.
About this series Sails.js, like its cousin Ruby on Rails, is a framework designed to help developers build web applications. Unlike Rails, which defaults to building applications that generate HTML on the server and ship it all back to the browser, Sails has great support for building HTTP APIs out of the box. This design encourages a better separation of concerns between client and server, in addition to promoting more interoperability across clients and servers. In this series, web developer and mentor Ted Neward introduces you to Sails.js. The HTTP API example app could be the back end for AngularJS, React, or virtually any other client application.
A cheese selection so abundant that it’s overwhelming is a perfect example of the paradox of choice, the principle that says that while having no choice is bad, having too many choices is equally as bad, and can sometimes lead consumers to give up choosing altogether.
Developers looking for a Node.js framework or library are faced with this very issue, a virtual embarrassment of riches. It can be difficult to know which of the many options to choose from, much less how to get started with a Node.js framework, or which factors will help you decide whether it’s the right fit.
Given the plethora of options, I had to wade through a few alternatives before settling on Sails.js. I like Sails because it embraces some of the ideas that have made the Rails framework so successful, while maintaining a distinct Node.js flair. In particular, Sails uses layered development and convention over configuration, two development techniques popularized by Rails and similar frameworks. Sails also integrates several other Node.js packages for key components, which means you don’t necessarily have to learn the framework from scratch. Another plus is the fact that the documentation for Sails.js is continually improving, a key indicator that I look for from any open source project.
In this first installment, I’ll focus on how Sails approaches layered development. Future installments will introduce convention over configuration and other conveniences of coding with Sails.
The Sails approach to layered development is different from what many traditional web developers might expect. In fact, Sails is one of an emerging breed of frameworks that redefines expectations of web application development by turning MVC on its ear. Instead of the MVC components residing on the server, as in a traditional MVC architecture, Sails puts the models and controllers on the server, while the views live on the client tier. Sails also sees the “view” as a more generic concept; it could be HTML, but it could also be a mobile application written in native iOS or Android.
This reconfiguring of the traditional MVC architecture achieves the very thing that architects and developers have long sought from application servers: a single place for business logic and validation, and a common interface for multiple clients.“
What about Mean.js Scott Davis recently introduced the MEAN stack and the Mean.js framework to developerWorks readers. I’ve done some work with Mean.js and maintain a project I built using it, but I’ve found that I don’t care for some of its underlying design decisions. In particular, Mean.js views the client and server as a sort of monolithic whole, whereas I embrace a more layered approach. I’ll explain more about that later in this tutorial.
This reconfiguring of the traditional MVC architecture achieves the very thing architects and developers have long sought from application servers: a single place for business logic and validation, and a common interface for multiple clients. The server needs only to project data elements in a representation recognizable to the client, and this approach provides an easy way to do traditional business logic and data validation through HTTP endpoints.
This new architecture, what I call an HTTP API, is similar to the RESTful style, but it’s not actually REST — not, at least, according to Roy Fielding’s definition of REST. REST relies on the exchange of hypermedia as the principal vehicle of application state (this is the Hypertext As The Engine Of Application State, or HATEOAS, principle — quite possibly the worst acronym ever invented). But an HTTP API system surrenders some of the flexibility and “generic client” capability that REST offers in exchange for a simpler model.
Unlike a REST system, HTTP API clients do require some deductive knowledge of the system they’re communicating with. They need to know the expected results and available endpoints ahead of time. It’s contrary to a REST architecture, but I find that the trade-off for simplicity is worthwhile.
Evolving REST HTTP APIs are used by most major API-based online systems. CouchDB, EventBrite, Yelp, and GitHub all are using an HTTP API design that is just a few steps shy of a truly RESTful system. Prismic, an HTTP API CMS system, was my inspiration for the example I’ll develop in this series.
In an HTTP API, all communication between client and server takes place over HTTP. For the example app, I’ll use JSON as the data format exchanged and standard HTTP verbs to indicate actions (GET as read, POST as insert, PUT as update, and DELETE as delete). I’ll also use HTTP headers as the mechanism for conveying out-of-band information like authentication data and so on. I will, however, still store some state data on the server side (contrary to a traditional REST design), and the data exchanged will all be JSON and assumed to be specific to this system.
While evaluating Sails.js, I spent some time experimenting with it. So without further ado, let’s start learning the ropes of Sails.
I’m going to expect that you are somewhat familiar with the principles and terminology of REST and RESTful architectures; you don’t have to be a zealot, but you should be comfortable using HTTP “in the raw” to submit GET requests, POST requests, and so on. The idea of looking at an in-flight HTTP request shouldn’t scare you.
For your development environment, make sure Node.js is up to date on your machine (as of this writing, mine reports version 0.12.7), and that you have the latest version of npm installed (mine is version 2.12.1). While the Node.js community obviously tries to keep versions backward-compatible, there’s no guarantee that the code I write here will work with significantly earlier versions of the technology. Caveat emptor.
The only installation required to use Sails.js is to install it globally to the npm cache, like so: npm install -g Sails.js.
npm install -g Sails.js
While you’re waiting for the Sails.js framework to download and install, take a minute to be sure that you have MongoDB set up in your development environment. If you don’t, now is a good time to download an installation appropriate for your target platform.
Note that Sails.js uses more than just a MongoDB back end; in fact, right out of the box, Sails.js uses a lightweight disk-based format. It’s not a recommended setup for production use, but works delightfully well for examples like this one. It will take a minute to get to the part of this series where MongoDB comes in, but you’ll be glad to have it when I do.
That’s the basic setup for now. You’ll need to add a Cloud Foundry install and an IBM Cloud login to the mix later in this tutorial, when I deploy the local app to the IBM Cloud.
Is Sails.js done installing? Verify it with the command sails version.
As of this writing, the latest version of Sails.js is 0.11.0. As long as the above command yields anything other than an error, Sails is installed and ready to go.
Tradition holds that the very first thing a programmer does when learning a new language or environment is to write the simplest possible program in that language: a Hello World application. Fortunately, Sails generates a Hello World as the basic application scaffolding when you create a new app. I can use this to verify that the Sails infrastructure is working. In a nod to agile development strategies, I’ll also deploy the scaffolding to the IBM Cloud as part of a “release early, release often” strategy.
Getting a new Sails.js app is pretty simple. Note that by default, the scaffolding will generate a more traditional web application, where the server-generated HTML is sent back to the client. While that’s useful in its own right, if the goal is to build an HTTP API app that is client-agnostic, I want to ensure that there’s no accidental “leakage” tying the client to the server. The best way to do that is to build the server with zero client components anywhere in the project. In Sails.js, that’s as simple as setting your default configuration to --no-frontend.
The downside of using the --no-frontend option is that I won’t have a visual way to verify that the scaffolding works. I’ll want that verification when I deploy the app to the IBM Cloud. So for this first installment, I’ll create an app that has the pretty HTML front-end scaffolding, but in future installments, I’ll run with the assumption that the app was built without any front-end pieces. (The difference is just what’s populated in the generated views directory, so it’s not as big a deal as it may seem.)
Enough talk. Let’s get sailing!
From the directory where you want the code to live on your development machine, issue a new command via the sails command line, then name your app: sails new
After you do this, Sails will think for a second, then should respond with a message like: “Created a new Sails app ‘hello-sails’!” (If anything else comes back, something is probably wrong with your Sails.js installation.) In the target directory, which is inferred from the application name, you’ll see that Sails.js laid down a number of directories and files. I’ll explore most of these in greater detail throughout the series. For now, I’ll just familiarize you with the basics of a Sails application.
Generally, it’s safe to assume that the scaffolding won’t have any problems (so long as nobody touches it), but it’s helpful to know how to run the app locally. So let’s kick off our journey with a “sails lift.”
Figure 1. Running Sails.js locally
Pretty, no? Personally, I like the use of color, and additionally, it’s quite the impressive display of ASCII art. More important is that this little ASCII painting confirms that Node.js is successfully running our scaffolded application on the local machine, on port 1337. Hit it with a browser and the default Sails.js web page will appear, complete with links back to the main Sails homepage and documentation.
About the production UI Selecting a --no-frontend Sails app will generate the same homepage as a more traditional web app; the UI will just be thin, with loads of client-side assets missing. As you get to know Sails, it’s worthwhile to experiment by generating both a traditional web app scaffold and one without the front end. You’ll learn for yourself what’s there and what’s missing from the HTTP API setup.
Running on a local machine is not the same as deploying your code to a production (or production-like) server, and this is where the cloud comes into play. For this series, I’ll use IBM Cloud as the cloud host. It seems apropos, and it’s an easy platform to use for hosting Node.js-based applications.
IBM Cloud also happens to be based on Cloud Foundry, so if you haven’t already, now is the time to install the Cloud Foundry client (cf). You’ll want it running on the same machine where your Sails.js code is hosted.
After you’ve installed the client, verify the installation by running cf--version; at the time of this writing, the version echoed back was 6.12.2.
As mentioned, you’ll also need an IBM Cloud login to deploy the app to the IBM Cloud. Once you’ve got your credentials, log in to the IBM Cloud and push the code from your local hello-sails directory into the IBM Cloud.
Teds‑MBP15:hello‑sails ted$ cf login
API endpoint: https://api.ng.bluemix.net
Select an org (or press enter to skip):
Targeted org developerWorks
Targeted space Ted Neward
API endpoint: https://api.ng.bluemix.net (API version: 2.27.0)
Space: Ted Neward
The cf tool will also need to know the API endpoint to use for deployment. If cf can’t figure that out, you can manually set the endpoint (api.ng.bluemix.net) via the command cf api api.ng.bluemix.net.
cf api api.ng.bluemix.net
Once you’ve logged in and set the API endpoint, you’ll use cf
push to deploy the scaffolded Sails.js app to the cloud. Cloud Foundry will just need a few parameters in order to deploy the application correctly. Rather than typing those repeatedly (which grows tiresome), create a manifest file (manifest.yml). This will contain information like the application name to use on the IBM Cloud, the hostname to use for a subdomain (if it’s different from the application name), how much RAM to reserve for executing, and so on.
The simple manifest file for the example:
‑ name: tedneward‑sailsIntro
command: node app.js
More about YAML Note that the manifest’s format is Yet Another Markup Language (YAML), which has been popularized by Ruby. Learning YAML isn’t necessary to follow the example for this series. Interested readers can find a full reference to the manifest’s settings on Cloud Foundry’s GitHub repository.
Another useful file that Cloud Foundry recognizes is .cfignore. This contains the list of files or directories that shouldn’t be pushed up from the local app to the cloud. For now, just file that information away for future reference.
With all that in place, all that’s left is to do a cf
push and let the push do its thing. (The output from this can be sometimes quite impressive in length, as the code must be packaged up, uploaded, unpacked, then installed on the cloud instance, which in turn means running an npm install on the cloud instance, which generates a ton of results. As a result, only about the first 20 and the last half-dozen lines are displayed here.)
Using manifest file /Users/ted/Projects/sails‑intro/code/hello‑sails/manifest.yml
Updating app tedneward‑sailsIntro in org developerWorks / space Ted Neward as email@example.com...
Using route tedneward‑sailsIntro.mybluemix.net
Uploading app files from: /Users/ted/Projects/sails‑intro/code/hello‑sails
Uploading 31M, 11773 files
Stopping app tedneward‑sailsIntro in org developerWorks / space Ted Neward as firstname.lastname@example.org...
Starting app tedneward‑sailsIntro in org developerWorks / space Ted Neward as email@example.com...
‑‑‑‑‑> Downloaded app package (24M)
‑‑‑‑‑> Downloaded app buildpack cache (18M)
‑‑‑‑‑> IBM SDK for Node.js Buildpack v2.5‑20150902‑1526
Based on Cloud Foundry Node.js Buildpack v1.5.0
‑‑‑‑‑> Creating runtime environment
‑‑‑‑‑> Installing binaries
engines.node (package.json): unspecified
engines.npm (package.json): unspecified (use default)
. . .
App tedneward‑sailsIntro was started using this command node app.js
Showing health and status for app tedneward‑sailsIntro in org developerWorks / space Ted Neward as firstname.lastname@example.org...
requested state: started
usage: 256M x 1 instances
last uploaded: Mon Oct 5 01:36:54 UTC 2015
buildpack: SDK for Node.js(TM) (ibm‑node.js‑0.12.7)
Assuming you’ve uploaded and installed all the components correctly, you should see a new subdomain on the IBM Cloud (in my case, it’s http://tedneward-sailsIntro.mybluemix.net). The new subdomain will respond to an HTTP GET / request by producing the Sails.js sample hello page.
With the scaffolding in place, I’m set up to begin extending and improving the example app. Thanks to the work done so far, I’ll be able to deploy the new code to the IBM Cloud each time I finish a new segment. Remember the principle, “always be shipping” — or put another way, “release early, release often.” Each time I push code to the server for others to use, I’m creating opportunities for people to find bugs, verify that I’m headed in the right direction with the code, and so on. Using a well-established cloud foundation makes it trivial to ship, since I’ve done most of the work up front. With a simple cf push, the latest version of the Sails app will be aloft and waiting for HTTP clients to find and use it.
The next installment will feature the Sails.js models. Getting to know the models will also introduce you to a hefty amount of Sails convention, called blueprints or the Sails Blueprint API. Like its inspiration, Rails, Sails favors convention over configuration, which means I’ll be able to build models, and get some useful functionality out of the box, without having to write a ton of configuration files or code.
I have just a scaffold now, but the example app that will take shape over the next several tutorials will be a blog engine — with a twist. Rather than build the engine as a traditional server-side web app, I’ll develop it as an HTTP API. The API packs all the technology I’ll need into the back end, leaving the client-side code to be determined by whatever front-end tool somebody wants to write.
For now, it’s time to pause. Given the name of the framework, let us not say “so long” but the more appropriate “bon voyage.”
March 15, 2019
April 12, 2019
Back to top