Community Post

Building fullstack web app with sails.js and Angular2

HaiderMalik

What is Sails.js ?

Sails is a Javascript framework designed to resemble the MVC architecture from frameworks like Ruby on Rails. It makes the process of building Node.js apps easier, especially APIs, single page apps and realtime features, like chat.

What we will Build ?

In this part we will build a rest api for blog.

Each user can create many blog posts.There is a one to many relationship between user and post. Each category has many posts,One to many relationship between category and post

Getting Started with Sails cli

To install sails.js you must have installed Node.js on your machine.

npm install sails -g

You can create a new project with sails new projectName

sails new blog-api --no-frontend

We are going to create sails project without frontend code because we will build frontend part in Angular2 . I will discuss more about Angular2 in part2 .

App Structure

Click here to learn more about app structure of Sails.js. App Structure

Connecting mongodb to sails.js

we are going to use mongodb as a database.You can use any database to build your backend rest api. Sails provide more than 30 adapters like sails-mysql, sails-mongodb, sails-redis. Click here to read more about database adapters. Database Adapters To install sails-mongo adapter

npm install --save sails-mongo

Add connection configuration for mongodb in config/connections.js

//config/connections.js
mongodbServer: {
    adapter: 'sails-mongo',
    host: 'localhost',
    port: 27017,
    // user: 'username', //optional
    // password: 'password', //optional
    database: 'blog-api' //optional
  }

Make sure you run mongodb from your terminal or command prompt. In first terminal you will have to run this command.

mongod

Open another terminal and run this command

mongo

Your mongodb will be running on port 27017. Create a database by running this command

use blog-ap

Add mongodb connection to models.js

//config/models.js
module.exports.models = { 
   connection: 'mongodbServer',
   migrate: 'alter'
};

Set default connection in development.js

//config/env/development.js
module.exports = {

  models: {
    connection: 'mongodbServer'
  }
};

Creating models

Create a new file User.js in api/models


module.exports = {

 tableName:"users",

  attributes: {

    first_name : { type: 'string', required: true },

    last_name : { type: 'string', required:true },

    age : { type  : 'integer'},

     posts:{
       collection: 'post',
       via:'user'

     }
  }
};

I have created one to many relationship between users and posts. I have defined posts collection in user model because we can populate all the posts for each user by using user.find().populate('posts') . It will return all the posts for each user.

Create a new file Post.js in api/models


module.exports = {

 tableName:"posts",

  attributes: {

    title : { type: 'string', required:true },

    content : { type: 'longtext' ,required:true },

    //Associations
    category : { model :'category', columnName:'category_id', required:true},

    user : { model :'user', columnName:'user_id', required:true},
  }
};

When we will create a new posts we also need to add id of category model and user model Create a new file Category.js in api/models

module.exports = {

  tableName:"categories",

  attributes: {

    name : { type: 'string', unique: true, required: true },

    posts: {
      collection:'post',
      via:'category'
    }
  }
};

We can also populate all the posts for each category

Defining Routes

/**
 * Route Mappings
 * For more information on configuring custom routes, check out:
 * http://sailsjs.org/#!/documentation/concepts/Routes/RouteTargetSyntax.html
 */
module.exports.routes = {

  //user
  'POST /user': 'UserController.create',

  //post
  'POST /post': 'PostController.create',
  'GET /posts': 'PostController.findAll',
  'GET /posts/:id': 'PostController.findOne',
  'DELETE /posts/:id': 'PostController.delete',
  'PUT /posts/:id': 'PostController.update',

};

Create new User

To create new user .We need to create a new UserController.js file in api/controllers


module.exports = {
  /**
   * It will create a new user .
   */
  create: function (req, res) {

    let firstName = req.param('first_name'),
        lastName = req.param('last_name'),
        age = req.param('age');

     if(!firstName){
       return res.badRequest({err:'Invalid first_name'});
     }

     if(!lastName){
       return res.badRequest({err:'Invlaid last_name'});
     }

    User.create({
     first_name : firstName,
     last_name : lastName,
     age:age
    })
    .then(_user => {
      if(!_user) return res.serverError({err:'Unable to create user'});

       return res.ok(_user); //to learn more about responses check api/responses folder
    })
    .catch(err => res.serverError(err.message));
  }
};

User.create() will return a promise. Click here to learn more about promises Promises .If you want to test this route you can use postman to test your rest-api. Postman is a chrome extension. If you have google chrome you can install postman very easily. We have defined an action method for each route.

Let us create new PostController.js in api/controllers . This time I will create a new controller by using sails cli To create PostController.js run this command

sails generate controller Post create findAll findOne delete update

Create a new post


module.exports = {

  /**
   * This method will create a new post for user
   */
  create: function (req, res) {

    let title = req.param('title'),
      content = req.param('content'),
      userId = req.param('user_id'), //original user_id from mongodb user model
      categoryName = req.param('category_name');

    if (!title) return res.badRequest({ err: 'Invalid post title field' });
    if (!content) return res.badRequest({ err: 'Invalid post content field' });
    if (!userId) return res.badRequest({ err: 'Invalid user_id field' });
    if (!categoryName) return res.badRequest({ err: 'Invalid category_name field' });

    Category.findOrCreate({ name: categoryName })
      .then(_category => {

        if (!_category) throw new Error('Unable to create category record');
        return _category;

      })
      .then(_category => {

        return Post.create({
          title,
          content,
          user: userId,
          category: _category.id
        });

      })
      .then(_post => {
        if (!_post) throw new Error('Unable to create new post');
        return res.json({ post: _post });
      })
      .catch(err => res.serverError(err.message));
  }
}

Update Post

Sails cli already created a dummy or empty update method for us. Refactor existing update() in api/controllers/PostController.js

/**
   * This method will update the post
   */
  update: function (req, res) {

    let title = req.param('title'),
      content = req.param('content'),
      userId = req.param('user_id'),
      categoryId = req.param('category_id'),
      postId = req.params.id;

    if (!postId) return res.badRequest({ err: 'post id is missing' });

    let post = {};

    if (title) {
      post.title = title;
    }
    if (content) {
      post.content = content;
    }
    if (userId) {
      post.user = userId;
    }
    if (categoryId) {
      post.category = categoryId
    }

    Post.update({ id: postId }, post)
      .then(_post => {

        if (!_post[0] || _post[0].length === 0) return res.notFound({ err: 'No post found' });

        return res.ok(_post);

      }).catch(err => res.serverError(err));
  },

Delete Post

Refactor existing delete() in PostController.js

/**
   * This method will delete the post
   */
  delete: function (req, res) {
    let postId = req.params.id;

    if (!postId) return res.badRequest({ err: 'missing post_id field' });

    Post.destroy({ id: postId })
      .then(_post => {
        if (!_post || _post.length === 0) return res.notFound({ err: 'No post found in our record' });
        return res.ok(`Post is deleted with id ${postId}`);
      })
      .catch(err => res.serverError(err));
  },

FindAll Posts

To get all posts you need to write code in PostController.findAll() in api/controllers/PostController.js

  findAll: function (req, res) {

    Post.find()
      .populate('user')
      .populate('category')
      .then(_posts => {

        if (!_posts || _posts.length === 0) {
          throw new Error('No post found');
        }
        return res.ok(_posts);

      })
      .catch(err => res.serverError(err));
  },

Find Single Post

To get post by id you need to write code in PostController.findOne()

 /**
   * find single post based on id
   */
  findOne: function (req, res) {

    let postId = req.params.id;

    if (!postId) return res.badRequest({ err: 'missing post_id field' });

    Post.findOne({ id: postId })
      .populate('category')
      .populate('user')
      .then(_post => {

        if (!_post) return res.notFound({ err: 'No post found' });

        return res.ok(_post);
      })
      .catch(err => res.serverError(err));
  }

Originally published at fullstackhour.com on April 3, 2017. In the next part we will create a frontend in Angular2 & Angular4.

HaiderMalik

2 posts

Full-Stack Javascript Developer.

Node.js , Sails.js , React.js , Redux, Angular2