Saturday, 11 April, 2015 UTC


Summary

What is it you like the most about Ember? What is it that convinced you, that in the plethora of frameworks out there, Ember is the one that you should be spending your precious time with?
For me it's all about ease of use. Coupled with ember-cli a lot of the tedious boilerplate code is abstracted away. Ember is opinionated, Ember embraces conventions. Using Ember allows me to focus on writing business logic instead of writing the same glue code over and over again. Ember-cli's generators allow me to automatically generate parts of my application and then edit them as needed. Ember has increased my productivity.
I recently discovered Sails, and we've hit it off.
Sails is like Ember, but for REST Apis.
More specifically Sails.js is a server side web framework built on node.js, that we can use to build REST APIs (along with rendering views, but I'm more interested in using Ember for that).
Sails makes it easy to build custom, enterprise-grade Node.js apps. It is designed to emulate the familiar MVC pattern of frameworks like Ruby on Rails, but with support for the requirements of modern apps: data-driven APIs with a scalable, service-oriented architecture. It's especially good for building chat, realtime dashboards, or multiplayer games; but you can use it for any web application project - top to bottom. sailsjs.org
Why is it like Ember? It abstracts boring boilerplate code away so you can focus on writing the business logic of your application. It's somewhat opinionated, and it embraces convention. And like ember-cli, it has generators that you can use to automatically generate parts of your application and then edit them as needed. Sails will very likely increase your productivity.
If you dont see the value of this, I want to ask you one question:
How many times have you had a great or fun idea.. But then when it's come time to sit down in front of a computer and build it you've thought to yourself: Oh man, first I have to create this whole stack and do all of these chores first before I can even start writing the creative stuff?
This has stopped me dead in my tracks before, more than once, by the time I've done my chores the impulse has gone.
Anyway, enough evangelism[1]. Let me show you how how nicely sails and ember work together:
[1] Rambling.

Disclaimer, before we begin.

I'm not an expert on Sails. I'm still very much learning it's quirks and configuration, I'm just a guy that got excited enough to write something about it.
I'm going to assume you already know how to use both Ember and ember-cli well enough to set up a project, and get data from a REST API.

Installing Sails

Installing Sails is as easy as installing any other node package. Just use npm.
> npm install -g sails
This gives us a new command to play with on the command line called sails, we can double check it's installed by requesting the version.
> sails -v
At the time of writing Sails is at version 0.11.0.

New Project

We'll keep the server and client projects separate, Ember will simply treat our API the same way as any other third-party API, that's just how I like to work. So first let's create a folder to represent our project:
> mkdir ember-and-sails
> cd ember-and-sails

Building the Server Code and the API

Next we'll create our server module. Users of ember-cli will be comfortable here, we can create a sails project with one simple command:
> sails new server
This will head off and create a server directory inside of the ember-and-sails dir. It will also give us a bunch of files.

Configuring the API.

From this point the first time around I was pretty eager to generate me some api and have a party. Which is exactly what I did, minus the party. It works just fine, but I didn't really agree with a few of the default settings.
By default the route of all your API calls will be stuctured in this way http://domain.com/[entity] where entity is the item were requesting, like person or cheesecake.
That might be fine for you, but I like my APIs to be a little more self describing than this, something like http://domain.com/api/version/[entity] will suit me.
Luckily, its very easy to do some configuration, however I've found its best do this configuration before generating anything.
To configure the api routes we need only edit one file, located at config/blueprints.js. Look for a key-value pair called prefix: '', it's probably commented out.
Uncomment this, and add whatever prefix takes your fancy, in my case I added '/api/v1'. This file also allows you to pluralize the route names and whatnot, have a look around, it's very well commented.

Generating an API

Configuration done! Let's generate an api:
> sails generate api todo
That's right, I'm being imaginative and creating a todo application!
The command above will create two files for us, a controller and a model, as well as setting up the necessary glue code that we need to have a working REST api. I won't go too much into detail about the specifics here. But it's cool that it was that easy!
To run our server we enter the following command:
> sails lift
You may get a prompt at this point asking you how you want to perform migrations, you'll get this prompt whenever you make a change to the database schema, such as adding a model, or changing a property on a model. For now just go for option 3, so we know we're starting fresh.
We'll get some pretty ASCII art of a boat, and instructions to go to http://localhost:1337. If we navigate to that in the browser, you'll see the default page. If you nativate to http://localhost:1337/todo, you'll be given an empty array in JSON format. Our API is working! But there's no data...

Adding Data to the Database

"What database?!" I hear you cry, "we haven't created or even specified a database!"
Well, Sails comes pre-packed with it's own ORM, called Waterline. So if there isn't a database found, Waterline will go off and create one as soon as it feels the need to, based on a schema that you provide.
By default this database is a file based affair that is stored in a temporary folder. But it is possible to add your own Postgres, Mongo, MySQL or SqlServer connection and work from there.
For this example I'm happy just using the temporary solution.
The generated API already has the endpoints that we need, which means we can use a REST client like Postman to send a requests to our API.
For example, if I sent a POST request to http://localhost:1337/todo passing the following data:
task: Learn how Ember and Sails work together
It will be perfectly valid. You'll be sent a response with the, now saved, JSON object. Complete with it's very own id and other handy data that you didn't specify.
Sending a GET request to the same URL now will return the following:
[
    {
      "task": "Learn how Ember and Sails work together",
      "createdAt": "2015-04-09T15:24:34.586Z",
      "updatedAt": "2015-04-09T15:24:34.586Z",
      "id": 1
    }
]
You'll notice the createdAt, updatedAt, and id fields have been added by Sails/Waterline for us, which is nice. You'll also notice we didn't even need to declare a schema for this to work (though you definitely should, that way you can do validation on the model and it's just a whole lot cleaner).
If you're wondering where this data is being stored, like I was, in this case you'll find it in the .tmp/localDiskDb.db file in the same place as all your other code.
Well, there you have it, we've created a full blown REST API in a matter of minutes. It's not going to be a problem to at this point go in and customise it all we want, but I'll save that for another day. Let's get to work on the client.

Building the Client

We'll cd back into the projects root directory and use ember-cli to create an ember app called client.
> ember-new client
Then we'll add the necessary route, controller and template to make all of this work. I'll also add in bootstrap for some styling.
Like I said, I'm going to assume you already know how this works, as it's all just basic Ember. So I wont explain it here, i'll just show you the code.
app/templates/application.hbs
<div class="container">
  <h1 class="text-muted">Things that need doing...</h1>

  {{outlet}}
</div>
app/routes/index.js
import Ember from 'ember';

export default Ember.Route.extend({
  model() {
    return Ember.$.getJSON('/api/v1/todo');
  }
});
app/controllers/index.js
import Ember from 'ember';

export default Ember.Controller.extend({
  actions: {
    createTodo() {
      var todo = {
        task: this.get('task'),
        isComplete: false
      };

      this.set('task', '');

      Ember.$.post('/api/v1/todo', todo)
        .then((newTodo) => {
          this.get('model').pushObject(newTodo);
        });
    },

    completeTodo(todo) {
      this.get('model').removeObject(todo);
      Ember.$.ajax({
        url: '/api/v1/todo',
        data: todo,
        method: 'DELETE'
      });
    }
  }
});
app/templates/index.hbs
<div class="row">
  <div class="col-md-12">
    <ul class="list-group">
      {{#each model as |todo|}}
        <li class="list-group-item">
          <div class="pull-right">
            <button class="btn btn-sm btn-success" {{action "completeTodo" todo}}>
              &check;
            </button>
          </div>
          {{todo.task}}
        </li>
      {{/each}}
    </ul>
  </div>
</div>

<div class="row">
  <div class="col-md-12">
    <form {{action "createTodo" on="submit"}}>
      {{input value=task class="form-control"}}
      <button type="submit" class="btn btn-block btn-primary">
        Add Item
      </button>
    </form>
  </div>
</div>

Running the Application

In order to run the application we need to start up the Sails server first:
> cd server
> sails lift
By default Sails will be running at http://localhost:1337.
We also need to run Ember separately, so in another terminal we can use ember-cli to start up a server for us, it is important that we proxy all requests to the Sails server for our app to work correctly, luckily we can do this all in one simple command:
> cd client
> ember server --proxy http://localhost:1337
If we navigate to http://localhost:4200 in the browser, where our Ember app is being hosted, you will see that we can communicate with the sails API in much the same way as any other third-party API.
I have published the above example to github if you'd like to check it out, clone it, do whatever.
If all of this still seems like too much work, check out the Sane Stack. It's an awesome tool some people much smarter than me have put together that integrates ember-cli and sails into a full JavaScript stack. It even has its own CLI to generate ember and sails stuff at the same time. Pretty cool stuff.