Sails JS Actions2 examples with CRUD and helper function

Author - Nirav Adatiya

A guide for Sails JS actions2 examples with crud and helper function, this includes how to use GET POST PUT DELETE with actions2. Example of exits and intercept is included.

Installing and creating Sails.js project

npm install sails -g
sails new sails-1-actions-2-examples

Select empty project for purpose of this demo. Once created  fire ‘sails lift‘ and browse our sails project should be running on localhost:1337

Tip: you can use nodemon for automatic restarting of application.

Simple CRUD example with actions2

In order to use Sails js effectively we should always use it’s inbuilt Waterline ORM. Here I will only mention things we need for this demo. Waterline is very powerful just like laravel eloquent and provides much easy ways for insert, update, delete using model. It also provides methods like beforeCreate, beforeUpdate events by which we can do some general stuff like hashing of password in common place.

To generate our first User Model we will use Sails CLI. It will automatically generate related file and basic stub. Before we create our model I want to cover very important thing here.

So in every table we need fields like id(primary key auto increment), createdAt, updatedAt in every table we create. Sails provide very elegant way to handle it.

copy/paste below code in attribute array in  /config/models.js and sails will automatically add these fields in every table. Also createdAt and updatedAt dates will be updated automatically.

attributes: {
  createdAt: {
    type: 'number',
    autoCreatedAt: true,
  },
  updatedAt: {
    type: 'number',
    autoUpdatedAt: true,
  },
  id: {
    type: 'number',
    autoIncrement: true,
  },
},

We also need to uncomment migrate: ‘alter’ (development use only). This will allow Sails JS to alter table schema if there is change in model.

Now let’s generate our first model with

sails generate model user

This will generate /api/models/User.js file. Let’s add some columns to our table. To add columns we need to define attributes as below and sails will generate columns automatically for us.

module.exports = {
  attributes: {
    firstName: {
      type: 'string',
      required: true,
    },
    lastName: {
      type: 'string',
      required: true,
    },
   email: {
      type: 'string',
      required: true,
      unique: true,
      isEmail: true,
    },
    password: {
      type: 'string',
      required: true,
      protect: true
    },
  }
}


Generate our first action that creates user

=>  sails generate action api/user/create

This will generate file inside user folder: api/controllers/api/user/create.js

We also need to declare route inside router.js (config/routes.js)

=> 'POST /api/v1/user/create': { action: 'api/user/create' },

Adding validation with actions2 inputs

Instead of using regular res and req, in sails v1 we will use inputs and exits.
In inputs object just add following  (this validation will return error itself if invalid data received).

inputs: {
    firstName: {
      type: 'string',
      required: true,
    },
    lastName: {
      type: 'string',
      required: true,
    },
    email: {
      required: true,
      unique: true,
      type: 'string',
      isEmail: true,
    },
    password: {
      required: true,
      type: 'string',
      maxLength: 15,
      minLength: 6,
    },
  },

Now we’ll use waterline’s create with our model name,

Remember: we can get parameters value with inputs (for ex. inputs.firstName);

fn: async function (inputs, exits) {
   var userRecord = await User.create({
       firstName: inputs.firstName,
       lastName: inputs.lastName,
       email: inputs.email,
       password: inputs.password,
     });

   if (!userRecord) {
     return exits.invalid({
       message: 'invalid, problem creating user'
     });
   }
   return exits.success({
     message: 'User has been created successfully.',
     data: userRecord
   });
 }
};

If userRecord created successfully we will return success message with user’s data.

Using Intercept and Exits

If email id is not unique then our model driver will return error with code ‘E_UNIQUE‘ you can intercept and send your own message like this

.intercept('E_UNIQUE', (err) => {
        return exits.invalid({
          message: 'invalid',
          err: err
        });
      })
      .fetch();

We can also create custom exits (for ex. return invalid with status code 409)

exits: {
   invalid: {
     statusCode: 409,
     description: 'user create error' // this will not go in response
   },
},

To use custom exits just use

return exits.invalid();

We can also pass message with custom exit

return exits.invalid({
    message:'invalid request'
})

Update

Let’s quickly create actions for update the user

sails generate action api/user/update

Add it to our routes

'PUT /api/v1/user/update/:userId': { action: 'api/user/update' },

To get userId from url we will declare it inside our inputs object

inputs: {
    userId: {
      type: 'string',
      required: true,
    },
}

So we can get easy access of userId with ‘inputs.userId.

We will update user with waterlines update function along with our model name

var updatedUser = await User.update({
      id: inputs.userId
}).set({
     firstName: inputs.firstName,
      lastName: inputs.lastName,
      email: inputs.email,
      password:inputs.password
});

Tip: Get data after updating record without using ‘.fetch();

Just add fetchRecordsOnUpdate: true inside our user model ( /api/models/User.js ) with attributes this will always fetch data for you when object is updated.

Tip: By default remove password in response.
To remove password from response just add this function inside your user model ( /api/models/User.js ) after attributes object.

attributes:{
...
},
customToJSON: function () {
    return _.omit(this, ['password'])
},

Index / View

Create new action

=> sails generate action api/user/index

Add new route

=> 'GET /api/v1/user': { action: 'api/user/index' },

Inside fn add this logic, and we’ll get all users.

fn: async function (inputs) {
    var allUsers = await User.find({});
    return allUsers;
  }

Delete

Generate new action with

=>sails generate action api/user/delete 

and add it to our routes

=>'DELETE /api/v1/api/user/delete/:userId': { action: 'api/user/delete' },

Again to access userId we will add it to our inputs object and then just delete it with destroy function

var userRecord = await User.destroy({
    id: inputs.userId
});

Before starting helper functions flow we need some variables globally
for global variables we need to create development.js

 If you don’t have one in your project just create it manually at config/env/development.js

add some global parameter like

twilioSid: 'your twilio sid',
twilioauthToken: 'your twilio token',
twiliomobilenumber: 'your twilio mobile number'

We will use these params in our helper function

Create Helper Functions (for send SMS)

Let’s create demo that sends SMS with helper function. We will use Twilio API for this,

to install twilio just fire

=>  npm install twilio --save

after that we need to generate helper function

=> sails generate helper sendSms

it will generate send-sms.js (/api/helpers/send-sms.js) now we will add required inputs to our inputs object

inputs: {
    toMobileNumber: {
      type: "string",
      example: "+918401428558",
      description: "The mobile number to whom we need to send sms",
      required: true
    },
    msgBody: {
      type: "string",
      example: "Message here",
      description: "The mobile number to whom we need to send sms",
      required: true
    }
  },

Add some logic inside our fn:

fn: async function (inputs, exits) {
    // console.log("helper: send-sms: ", inputs);
    twilio_client.messages.create({
      body: inputs.msgBody,
      to: inputs.toMobileNumber, // Text this number
      from: sails.config.twiliomobilenumber //From a valid Twilio number
    }, function (err, message) {
      if (message) {
        return exits.success({
          status: true,
          message: "sms send success",
          data: message
        });
      } else if (err) {
        var err = new Error({
          type: 'Twilio',
          status: false,
          message: err.message
        });
        return exits.error(err);
      } 
    });
  }
};

Create action for sending sms

=> sails generate action sendSms

This will generate an action inside api/controller/send-sms.js, to access this action from route we will declare a route inside router.js

=>'POST /api/v1/send-sms': { action: 'send-sms' }
under send-sms action (/api/controller/send-sms.js) write

inputs: {
    toMobileNumber: {
      description: "Mobile Number to which we need to send test sms.",
      type: "string",
      required: true
    },
    msgBody: {
      type: "string",
      required: true
    }
  },
  exits: {
    success: {
      description: 'All done.',
    },
  },

  fn: async function (inputs,exits) {
    console.log('gone => ', sails.config.twilioSid, sails.config.twilioauthToken);
    var smsResponse = await sails.helpers.sendSms(inputs.toMobileNumber, inputs.msgBody);
    if (smsResponse) {
      console.log("action send-sms success smsResponse", smsResponse);
      return exits.success({
        Hi: smsResponse
      });
    }
  }

After adding this just call our API with two params

"toMobileNumber":"+91xxxxxxxxxx",
 "msgBody":"Testing my First SMS, yey it's working"

with sails.helpers.sendSms we will now able to send sms with twilio config,

Some useful links

I have created a POSTMAN for all API calls. Also, GitHub is ready to get going easily.

Don’t miss the next post!

Loading

Related Posts