Sails — Realtime MVC Framework For NodeJS

Krina Soni
6 min readOct 12, 2018
Let’s sail together !!

Sails is Web Framework.
Till now, when we hear the word, Framework- we think as front-end web framework, But sails is Back end-Web Framework. Sails helps you build APIs, server HTML files and handle simultaneous users.

Features

In market, there are lots of frameworks available, Hence before setting up environment for sails, let’s see what it offers and why should we use this framework.

Sails is a lightweight framework that sits on top of Express. Its ensemble of small modules work together to provide simplicity, maintainability, and structural conventions to Node.js apps.

This is what sails official website says.

Key features are it’s all Javascript, and you can work with any database as it bundles powerful ORM — waterline, which provides a simple data access layer that works with any database. Also it has supports for different adapters. For ex, MongoDB, PostgreSQL, MySQL, Redis & local disk. (also community provides more)

Another key feature is it’s Auto Generate API — Sails comes with blueprints that help start your app’s backend without writing any code. Just one command generate api for search, paginate, sort, filter, create, destroy, update.
sails generate api module-name

It also supports web sockets without additional code & sails is compatible with almost all front-end frameworks including Angular, React, Vue, Backbone etc.

Setup

To install sails, you need node in your system.
npm install -g sails // -g for global dependency
Sails provide its own CLI just as create-react-app.

Let’s start building application in which we’ll cover it’s core concepts and create api for users login, signup, get users & add user.

Sails support different database adapters such as MySQL, PostgreSQL, MongoDB which you have to install in order to use them.

Available Adapters
(P.S : I have used sails-mongo & for mongo database you can use online mLab which will let you store data of 500MB free.)

To configure the adapter, you have to pass waterline adapter as adapter & connection URL which you can do in datastores.js under config folder.

adapter: ‘sails-mongo’,
url: ‘your connection url goes here’

Now, you’re all setup for routes and services.

Routes

When you call api, you’ll be calling one url which is route for that api and important task is on that which actions to call or views to render.
Sails provide two types of routes: custom & automatic.

Sails structure provides router file in config folder. /config/routes.js

While we want to do signup & login, We have to create routes for that in this file. It should look like as follow:

module.exports = {
‘/’: {
view: ‘pages/homepage’
},
//authentication routes
'POST /signup' : 'AuthController.signup',
'POST /user-login' : 'AuthController.login'
}

Here, AuthController is controller name and signup is method name.
On Post request with url /signup This method will get executed.

Now, on signup we need to store password with hash and in login we have to generate token and manage it. For that, I have used jwt and bcryptjs.

To install both, npm install jsonwebtoken bcryptjs — save

Services

Now, we’ll start coding in controller that will make an entry in database.

In order to make enrty, we have to create schema for particular model.
sails has built folder as model under api folder.
we’ll create one model for our user schema and define rules, column names & default values if need be.
User.js will look somewhat like this.

module.exports = {
attributes: {
email:{
required: true,
unique: true,
type:’string’,
},
password:{
required: true
},
name: {
required: true,
type:’string’
},
city: {
type:’string’,
defaultsTo:’’
}
}
}

For signup, we require email & password as required field and name & city can be empty.

Let’s create function for signup in AuthController.

signup: async function (req, res) {
if (req.body.email && req.body.password) {
let hashPwd = bcrypt.hashSync(req.body.password);
let finalUser = req.body;
finalUser[‘password’] = hashPwd;
let user = await User.create(finalUser).meta({ fetch: true });
if (user) {
return res.json({ message: ‘Signup successfull. Please login’ });
} else {
return res.json({ message: ‘Signup failed!!’ })
}
} else {
return res.json({ message: ‘Email or Password is not present.’ })
}

Let’s see what this function is doing, first it will check if email & password is present then only it will proceed further else it will return as send both.

Then comes the part where you have to store hashed password and you can use any method, I have used bcrypt js and it’s hashSync method.
For bcrypt reference.
let hashPwd = bcrypt.hashSync(req.body.password);
This line is creating alphanumeric long password and we’ll store it in database for security.
It’s time for Login.

login: async function (req, res) {
let user = await User.findOne({ email: req.body.email })
let hashPwd = bcrypt.compareSync(req.body.password, user.password);
if (hashPwd) {
let finalUser = user;
finalUser[‘token’] = jwt.sign(user.email, ‘secret’)
return res.json({ data: finalUser, message: ‘Login success’, status: ‘SUCCESS’ });
} else {
return res.json({ message: ‘Authentication failed!!’, status:
ERROR’ });
}
}

what we need to do in login is, check if email exist and password matches.
To match password we need to decypt the encrypted password.
bcrypt provide different method for that as well and I have used compareSync.
let hashPwd = bcrypt.compareSync(req.body.password, user.password);
We're checking with user sent password and database stored password & it will return boolean value.

Also we have to send token when user login and manage that throughout the session of the user.

I have used jwt to generate token and when user will call other services, it will come in headers and will check if that matches then only user will get access to the data.

I have created token with the use of one secret key and email. You can choose any secret key and that will be used to verify the email while accessing other apis. That we’ll see in next topic.

Policy

Policies in Sails are versatile tools for authorization and access control — they let you execute some logic before an action is run, to determine whether or not to continue processing the request. The most common use-case for policies is to restrict certain actions to logged-in users only.

This is what sails official documentation has to say about policies.
Which I mentioned earlier, we’ll use policy to manage access through token — before any api gets called and data is sent to users.

Sails provide policies.js under api folder where we’ll add file to check if user is authorized or not.

//isAuthorized.jsvar jwt = require(‘jsonwebtoken’);
module.exports = async function (req, res, next) {
var token;
if (req.headers && req.headers.authorization) {
token = req.headers.authorization;
let verified = await jwt.verify(token, ‘secret’);
if (verified) {
req.user = verified;
next();
} else {
return res.json({ message: ‘Token does not exist!!’ })
}
} else {
return res.json({ message: ‘Authentication failed!! Token Required.’ })
}
}

First, we’re taking headers.authorization & then verify that token using our secret key.
let verified = await jwt.verify(token, ‘secret’);
This will return true/false on basis of token verification & then if its verified then next() will proceed further else it will return failed message.

Now, question should be from where it will be called to check ? Or I have few services which should be called without token then what to do?
Let’s get to answers to these questions.

Applying policies to a controller

There’s policies.js under config folder, where we can configure our requirements for which controller — policy to use or not.

//policies.js
module.exports.policies = {
// ‘*’: true,‘AuthController’ : {
‘*’ : true
},
‘UserController’ : {
‘addUser’: [‘isAuthorized’],
},
};

In policies.js, default will be true for all which I have commented out.
Now, Once you make another controller and make entries to database, provide function name where you want to verify token.
For ex, I have two functions for get all users &add user. And in get users, I don’t want to verify token then I’ll provide one method and which policy to use.

Lifecycle callbacks

Lifecycle callbacks are functions that are called before or after certain model methods.

You can use for some operation before/after create, before/after update, before/after destroy.

You can do this in model itself with the definition of particular module.

Here you can find fully working api demo : Github
That’s all for now in sails. Dig up more.
If you like it, clap & spread it.
Thank you !!

--

--

Krina Soni

Passionate about places, photos and coding! Food is love !!