An Introduction to the MEAN Stack

The term MEAN stack refers to a collection of JavaScript based technologies used to develop web applications. MEAN is an acronym for MongoDB, ExpressJS, AngularJS and Node.js. From client to server to database, MEAN is full stack JavaScript. This article explores the basics of the MEAN stack and shows how to create a simple bucket list application.

Introduction

Node.js is a server side JavaScript execution environment. It’s a platform built on Google Chrome’s V8 JavaScript runtime. It helps in building highly scalable and concurrent applications rapidly.

Express is lightweight framework used to build web applications in Node. It provides a number of robust features for building single and multi page web application. Express is inspired by the popular Ruby framework, Sinatra.

MongoDB is a schemaless NoSQL database system. MongoDB saves data in binary JSON format which makes it easier to pass data between client and server.

AngularJS is a JavaScript framework developed by Google. It provides some awesome features like the two-way data binding. It’s a complete solution for rapid and awesome front end development.

In this article, we’ll be creating a simple CRUD application using the MEAN stack. So, let’s dive in.

Prerequisites

Before getting started, we need to install the various MEAN software packages. Begin by installing Node.js from the download page. Next, install download and install MongoDB. The install MongoDB page contains guides for setting up Mongo on a variety of operating systems. To make things easier, we’ll be starting from a MEAN boilerplate project. Simply clone the boilerplate repo and install the dependencies using npm as shown in the following listing.

git clone http://github.com/linnovate/mean.git
cd mean
npm install

This installs the required packages. Next, we need to set the default port on which MongoDB runs to 27017 as specified in the README file of the boilerplate. Open up the file /etc/mongodb.conf and uncomment the line port = 27017. Now, restart the mongod server as shown below.

mongod --config /etc/mongodb.conf

Next, from the project directory simply type grunt. If all goes well, you will see a message like this:

Express app started on port 3000

Now that the server is running, navigate to http://localhost:3000/ in a browser to see the boilerplate app running.

Boilerplate Overview

We now have a fully functional boilerplate application. It has authentication implemented, including using social media login. We won’t be going much into that, but will be creating our own little app. If you have a look at the application structure, the public folder contains our AngularJS front end and the server folder contains our NodeJS backend.

Creating a Listing View

First, let’s start by creating our front end using AngularJS. Navigate to the public folder. Create a new folder called bucketList, where we’ll keep our front end files. Inside the bucketList directory, create subdirectories named controllers, routes, services, and views. Inside the bucketList folder also create a file named bucketList.js containing the following code.

'use strict';

angular.module('mean.bucketList', []);

Next, open mean/public/init.js and add the module mean.bucketList. The modified portion should look like this:

angular.module('mean', ['ngCookies', 'ngResource', 'ui.bootstrap', 'ui.router', 'mean.system', 'mean.articles', 'mean.auth', 'mean.bucketList']);

Now, navigate to public/bucketList/routes and add the bucketList.js route file to handle routing in our app. The code to accomplish this is shown below.

'use strict';

//Setting up route
angular.module('mean.bucketList').config(['$stateProvider', '$urlRouterProvider',
  function($stateProvider, $urlRouterProvider) {
    // states for my app
    $stateProvider
      .state('all bucket list', {
        url: '/bucketList',
        templateUrl: 'public/bucketList/views/list.html'
      });
  }
]);

Inside public/bucketList/views/ create a file named list.html. This is our view, which will display our bucket list. The contents of this file are shown below.

<section data-ng-controller="BucketListController">
  Welcome to the bucket list collection
</section>

Also create a file named bucketList.js inside public/bucketList/controllers containing the following code.

'use strict';

angular.module('mean.bucketList').controller('BucketListController', ['$scope', '$stateParams', '$location', 'Global',
  function($scope, $stateParams, $location, Global) {
    $scope.global = Global;
  }
]);

Next, start the app using grunt. Make sure that MongoDB is running too if it’s not already. Navigate your browser to http://localhost:3000/#!/bucketList, and you should see the list view that we created. If you are wondering about the #! in the url, it’s just done to separate the AngularJS and NodeJS routing.

Add to the Bucket List

Let’s create a view to add things to our bucket list. Inside public/bucketList/views add a new HTML file named create.html containing the following code.

<section data-ng-controller="BucketListController">
  <form class="form-horizontal col-md-6" role="form" data-ng-submit="create()">
    <div class="form-group">
      <label for="title" class="col-md-2 control-label">Title</label>
      <div class="col-md-10">
        <input type="text" class="form-control" data-ng-model="title" id="title" placeholder="Title" required>
      </div>
    </div>
    <div class="form-group">
      <label for="description" class="col-md-2 control-label">Description</label>
      <div class="col-md-10">
        <textarea data-ng-model="description" id="description" cols="30" rows="10" placeholder="Description" class="form-control" required></textarea>
      </div>
    </div>
    <div class="form-group">
      <div class="col-md-offset-2 col-md-10">
        <button type="submit" class="btn btn-default">Submit</button>
      </div>
    </div>
  </form>
</section>

This code attaches the BucketListController controller. Also note that on form submit, a method named create() is invoked. Next, let’s create a method named create() in the BucketListController. The following code must be added to public/bucketList/controllers/bucketList.js, as shown below. We have injected the BucketList service in the controller, which we need to interact with the back end.

'use strict';

angular.module('mean.bucketList').controller('BucketListController', ['$scope', '$stateParams', '$location', 'Global', 'BucketList',
  function ($scope, $stateParams, $location, Global, BucketList) {
    $scope.global = Global;

    $scope.create = function() {
      var bucketList = new BucketList({
        title: this.title,
        description: this.description
      });

      bucketList.$save(function(response) {
        $location.path('/bucketList');
      });
    };
  }
]);

The contents of public/bucketList/services/bucketList.js are shown below.

'use strict';

angular.module('mean.bucketList').factory('BucketList', ['$resource',
  function($resource) {
    return $resource('bucketList);
  }
]);

We also need to add a route to add items to the bucket list. Modify public/bucketList/routes/bucketList.js, adding one more state as shown below.

'use strict';

//Setting up route
angular.module('mean.bucketList').config(['$stateProvider', '$urlRouterProvider',
  function($stateProvider, $urlRouterProvider) {
    // states for my app
    $stateProvider
      .state('all bucket list', {
        url: '/bucketList',
        templateUrl: 'public/bucketList/views/list.html'
      })
      .state('add bucket list', {
        url: '/addBucketList',
        templateUrl: 'public/bucketList/views/create.html'
      })
  }
]);

Restart the server and navigate to http://localhost:3000/#!/addBucketList. You should see the bucket list creation form. Unfortunately, it’s not yet functional. We need to create the back end too.

Creating the Back End

The bucket list should have a title, description, and status. So, create a new file called bucketlist.js in server/models/bucketlist.js, and add the following code.

'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
  Schema = mongoose.Schema;

/**
 * Bucket List Schema
 */
var BucketListSchema = new Schema({
  created: {
    type: Date,
    default: Date.now
  },
  title: {
    type: String,
    default: '',
    trim: true
  },
  description: {
    type: String,
    default: '',
    trim: true
  },
  status: {
    type: Boolean,
    default: false
  }
});

mongoose.model('BucketList', BucketListSchema);

We need to configure the Express route so that service calls from AngularJS are handled properly. Create a file named server/routes/bucketList.js containing the following code.

'use strict';

var bucketList = require('../controllers/bucketList');

module.exports = function (app) {
  app.post('/bucketList', bucketList.create);
};

POST requests to /bucketList are handled by the bucketList.create() method. This method belongs in the server controller, bucketList.js, that we still need to create. The contents of server/controllers/bucketList.js should look like this:

'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
  BucketList = mongoose.model('BucketList');

/**
 * Create an Bucket List
 */
exports.create = function(req, res) {
  var bucketList = new BucketList(req.body);

  bucketList.save(function(err) {
    if (err) {
      console.log(err);
    } else {
      res.jsonp(bucketList);
    }
  });
};

There’s still a lot to clean up, but we can check if its working as expected. When a user submits the AngularJS form, it calls the AngularJS service, which invokes the server side create() method, which then inserts the data into MongoDB.

After submitting the form, we can check if the data is properly inserted into Mongo. In order to check data in MongoDB, open another terminal and issue the following commands.

mongo                   // Enter the MongoDB shell prompt
show dbs;               // Shows the existing Dbs
use mean-dev;           // Selects the Db mean-dev
show collections;       // Show the existing collections in mean-dev
db.bucketlists.find()   //Show the contents of bucketlists collection

Creating the Bucket List View

First, add a new route in server/routes/bucketList.js:

app.get('/bucketList', bucketList.all);

This new route calls the controller’s all() method. Add this method to server/controllers/bucketList.js, as shown below. This code finds the entries in the bucketList collection and returns them.

exports.all = function(req, res) {
  BucketList.find().exec(function(err, bucketList) {
    if (err) {
      console.log(err);
    } else {
      res.jsonp(bucketList);
    }
  });
};

Next, add a new method in public/bucketList/controllers/bucketList.js as shown below.

$scope.getAllBucketList = function() {
  BucketList.query(function(bucketList) {
    $scope.bucketList = bucketList;
  });
};

This code fetches the data from Mongo and saves it in our $scope.bucketList variable. Now, we just need to bind it to our HTML. This is done in public/bucketList/views/list.html:

<section data-ng-controller="BucketListController" data-ng-init="getAllBucketList()">
  <ul class="bucketList unstyled">
    <li data-ng-repeat="item in bucketList">
      <span>{{item.created | date:'medium'}}</span> /
      <span>{{item.title}}</span>

      <div>{{item.description}}</div>
    </li>
  </ul>
  <a href="/#!/addBucketList">Create One</a>
</section>

Restart the server and navigate to http://localhost:3000/#!/bucketList. This should display the bucket list items. You can also try adding new items by clicking on the “Create” link below the list.

Conclusion

In this article, we focused on creating a simple app using the MEAN stack. We implemented adding an entry into MongoDB and displaying the entries from the DB. If you’re interested in extending this example, you can try adding the update and delete operations. The code from this article is available on GitHub.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Jovon Packard

    If you see an error message in your console about angular is undefined then you probably need to run ‘bower install’ also. Worked like a charm afterwards.

    • http://www.techillumination.in Jay

      Thanks for the feedback Jovon :)

  • Damian Kałek

    Thanks for article, good job.

    Only one syntax problem in – public/bucketList/services/bucketList.js
    exacly – return $resource(‘bucketList); -> lack apostrophe

    thx.

    • http://www.techillumination.in Jay

      Thanks Damian :)

  • http://www.techillumination.in Jay

    Thnks for the feedback Lior :)

  • Joseph Myalla

    I have tried to follow the article but each time i run grunt to test the setup i get an error that
    $urlRouterProvider is declared but never used, what could be the problem, i am using ubuntu 12.04