Sails.js

An Introduction

Nic Jansma | nicj.net | @NicJ

Sails.js is:

  • A MVC backend web framework for Node.js
  • Built on top of Express
  • Inspired by Ruby on Rails / Symfony / Zend
  • Convention over configuration
  • Ideal for chat, realtime dashboards and multiplayer games

Sails.js core features

  • 100% JavaScript
  • Database agnostic (includes custom ORM)
  • Auto-generated REST APIs
  • Easy WebSocket support and integration with REST
  • Reusable security policies
  • Front-end agnostic
  • Flexible asset pipeline (builds)

Getting Started


npm install -g sails
sails new [project path]
cd [project path]

Running Your New Web App


sails lift

info: Starting app...

info:
info:
info:    Sails              <|
info:    v0.10.2             |\
info:                       /|.\
info:                      / || \
info:                    ,'  |'  \
info:                 .-'.-==|/_--'
info:                 `--'-------'
info:    __---___--___---___--___---___--___
info:  ____---___--___---___--___---___--___-__
info:
info: Server lifted in `example1`
info: To see your app, visit http://localhost:1337
info: To shut down Sails, press <CTRL> + C at any time.

debug: --------------------------------------------------------
debug: :: Thu Aug 07 2014 06:43:55 GMT-0400 (Eastern Daylight Time)

debug: Environment : development
debug: Port        : 1337
debug: --------------------------------------------------------

http://localhost:1337

Generate Model and REST API

Let's create a new beer model and REST API


sails generate api beer

This creates skeleton files: api\controllers\BeerController.js and api\model\Beer.js

Try it out

http://localhost:1337/beer


[]

http://localhost:1337/beer/create?name=Centennial IPA&brewery=Founders&have=10


{
    "name": "All Day IPA",
    "brewery": "Founders",
    "createdAt": "2014-08-07T13:11:10.536Z",
    "updatedAt": "2014-08-07T13:38:21.517Z",
    "id": 1,
    "have": 10
},

Try it out

http://localhost:1337/beer


[
    {
        "name": "All Day IPA",
        "brewery": "Founders",
        "createdAt": "2014-08-07T13:11:10.536Z",
        "updatedAt": "2014-08-07T13:38:21.517Z",
        "id": 1,
        "have": 10
    }
]

What just happened

  • Sails created a Model (Beer.js) and Controller (BeerController.js)
  • Sails blueprints automatically configure new routes for the model (eg POST /beer and /beer/create).
  • Sails uses whatever storage layer you want for persistence

Anatomy of a Sails.js App

  • api/controller/ - controllers
  • api/models/ - models
  • api/policies/ - authentication / authorization
  • api/responses/ - res.xyz() handlers
  • api/services/ - services
  • assets/ - static assets
  • config/ - app config
  • tasks/ - grunt / cli tasks
  • views/ - views

Models


module.exports = {
  attributes: {
    name: {
      type: 'string',
      required: true
    },
    brewery: {
      type: 'string',
      required: true
    },
    have: {
      type: 'integer',
      defaultTo: 1
    }
  }
};

Controllers

Blueprint Routes

By default, Sails creates three types of blueprint routes:

  • RESTful routes for /:model (HTTP GET, DELETE, etc)
  • Shortcut routes to easily test your model via HTTP GET requests, such as /:model/delete/:id (should be turned off in production)
  • Action routes for any additional actions in your controllers

Authentication and access control are handled via policies

Blueprint Routes

RESTShortcut
QueryGET /api/:model
FetchGET /api/:model/:id
CreatePOST /api/:modelGET /api/:model/create
UpdatePUT /api/:model/:idGET /api/:model/update/:id
DeleteDELETE /api/:model/:idGET /api/:model/destroy/:id

Blueprint Actions

By default, Sails creates actions on your controllers for ORM functionality:

  • find
  • findOne
  • create
  • update
  • destroy
  • populate
  • add
  • remove

Custom Actions and Routes


// config/routes.js
module.exports.routes = {
  '/': {
    view: 'homepage'
  },
  'post /beer/:id/drink': 'BeerController.drink'
}

// BeerController.js
module.exports = {
  drink: function (req, res) {
    if (!req.params.id) { return res.badRequest('ID not supplied'); }

    Beer.findOne({ id: req.params.id }, function(err, model) {
      if (err || !model) {
        return res.badRequest('Beer not found');
      }

      model.have--;

      model.save(function(err) {
        return res.ok(model);
      });
    });
  }
};

Views


// BeerController.js
module.exports = {
  show: function(req, res) {
    Beer.find({}, function (err, beers) {
      res.view('show-beers', { title: 'Beers', beers: beers });
    });
  }
};

// show-beers.ejs
<ul>
<% for(var i=0; i<beers.length; i++) {%>
   <li><%= beers[i].name %></li>
<% } %>
</ul>

Socket.IO integration

Sails automatically translates incoming socket.io messages into Express requests

Also gives you Pub/Sub via res.broadcast() and req.join()


var socket = io.connect('http://localhost:1337');
socket.request('/beer', {}, function(beers) { console.log(neers); });

Links

Thanks - Nic Jansma - nicj.net - @NicJ