Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / Node.js

MEAN Stack Beginner Tutorial

4.75/5 (11 votes)
9 Nov 2015CPOL5 min read 101.1K   4  
In this article we learn MEAN Stack a combination of open source JavaScript framework. How they work and How can create a single page application using these technologies. We use these JavaScript technologies not only on front end but also on the back end.

Introduction

This article will teach you about MEAN Stack as the title show, MEAN is actually combination of 3 JavaScript frameworks and a NoSQL or document based Database technology. So here,

M is for MongoDB

MongoDB is an open source, document-oriented database designed with both scalability and developer agility in mind. Instead of storing your data in tables and rows as you would with a relational database, in MongoDB you store JSON-like documents with dynamic schemas.

E is for ExpressJS

Express.js is a Node.js web application server framework, designed for building single-page, multi-page, and hybrid web applications. It is the de facto standard server framework for node.js.

A is for AngularJS

AngularJS is a structural framework for dynamic web apps. It lets you use HTML as your template language and lets you extend HTML's syntax to express your application's components clearly and succinctly.Angular's data binding and dependency injection eliminate much of the code you would otherwise have to write.

N is for NodeJS

nodejs.org. Node.js is an open-source, cross-platform runtime environment for developing server-side web applications. Node.js applications are written in JavaScript and can be run within the Node.js runtime on OS X, Microsoft Windows, Linux, FreeBSD, NonStop, IBM AIX, IBM System z and IBM.

And actually, MEAN is

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.

Let me tell you how they work together, Angular is acually use for front end developement. I'll design views using Angular.js in order to render views on single page. For server side, I'll use Node.js which acutally internally use Express.js, Through express I'll write a API in order communicate with the database. In the end, I'll use MongoDb for storing data. As diagram shows

                                    

So let start from the very first.

Tools

Before continue, You must have Node installed, MongoDB Installed. You will find them here

Download Mongodb

Download Node

In this tutorial I'll use Visual Studio Code latest release 0.9.2, But you can use Sublime Text editor as well. I'll install packages through Node Package Manager using windows CMD prompt. You can install it very  easily using node cmd after install nodejs.

Background

The basic idea here is to start learning how MEAN Stack and Node work, a lot of basic questions on stackoverflow cause me to write this article. So this tutorial show you how to basically register a user and authenticate a user with their respected controllers and modules.

Using the code

I have Main directory named MeanApp. This root directory contains different sub directories, I'll describe some main directories.  

1) Angular will contain three other sub directories, Controllers for angular controllers, Modules and Models.

2) Public will contain all javascript libraries.

3) Routes will contain expressjs api's which will handle request and will intract with MongoDB.

4) Views will contain all respected views in their respected folders.

On the root folder I am using server.js for my startup file. So let's start it.

For installing other open source libraries into your application you just need to install them through npm package manager. using command npm install [package] --save

You need to run following command in order to install required tutorial packages

npm install express --save

npm install path --save

npm install morgan --save

npm install cookie-parser --save

npm install body-parser --save

npm install bcrypt-nodejs --save

npm install passport --save

npm install passport-local --save

npm install express-session --save

npm install mongoose --save

Let start from designing user interface, first create new modules.js file in Angular/Modules folder.

C++
//Angular Starter App
var main = angular.module("main", ['ui.router','ngRoute','ngResource'])
.run(function($http,$rootScope)
{
    if(sessionStorage.length > 0){
        $rootScope.current_user = sessionStorage.current_user;
        $rootScope.authenticated = true;
    }else{
        $rootScope.authenticated = false;
        $rootScope.current_user = 'Guest';
    }
    
    $rootScope.signout = function(){
        $http.get('auth/signout');
        $rootScope.authenticated = false;
        $rootScope.current_user = 'Guest';
        sessionStorage.clear();
    };

});                                                                                                     
//Routing Configuration (define routes)
main.config([
    '$stateProvider', '$urlRouterProvider', '$httpProvider',
    function ($stateProvider, $urlRouterProvider,$rootScope) {
        $urlRouterProvider.otherwise('/');
        $stateProvider
            .state('home', {
                url: '/',
                templateUrl: 'Index.html',
                caseInsensitiveMatch: true,
                controller: 'MainController'
            })
            .state('contact', {
                url: '/contact',
                templateUrl: 'Contact.html',
                caseInsensitiveMatch: true,
                controller: 'MainController'
            })
            .state('about', {
                url: '/about',
                templateUrl: 'About.html',
                caseInsensitiveMatch: true,
                controller: 'MainController'
            })
            .state('login',{
                url: '/login',
                templateUrl: 'login.html',
                caseInsensitiveMatch: true,
                controller: 'AuthController'
            })
            .state('register',{
                url: '/register',
                templateUrl: 'register.html',
                caseInsensitiveMatch: true,
                controller: 'AuthController'
            }).state('unauth',{
                url: '/unauth',
                templateUrl: 'unauth.html',
                caseInsensitiveMatch: true
            });
    }
]);                      

Now lets create a Model user.js in Angular/Models folder

C++
//create new model
var mongoose = require('mongoose'); //refering mongoose for creating user friendly class type model.
//defining schema for user model
var userSchema = new mongoose.Schema({
    username: String,
    password: String,
    email: String,
    role: String,
    created_at: {type: Date, default: Date.now}
    
});
mongoose.model('User', userSchema);
var User = mongoose.model('User');
exports.findByUsername = function(userName, callback){
User.findOne({ user_name: userName}, function(err, user){
        if(err){
            return callback(err);
        }
return callback(null, user);
});
}

exports.findById = function(id, callback){
User.findById(id, function(err, user){
        if(err){
           return callback(err);
        }
         return callback(null, user);
    });
}

We have done with our Model, Now create two new Controllers AuthController.js and MainController.js in Angular/Controller folder.

C++
//auth controller
main.controller("AuthController", function ($scope, $http, $rootScope, $location) {
$scope.user = {username: '', password: ''};
$scope.error_message = '';
//login call to webapi (node implemented service)
$scope.login = function(){
        $http.post('/auth/login', $scope.user).success(function(data){
        if(data.state == 'success'){
                $rootScope.authenticated = true;
                $rootScope.current_user = data.user.username;
                $rootScope.sess = data.user;
                sessionStorage.setItem('current_user', $rootScope.sess.username);
                $location.path('/');
                }
            else{
                $scope.error_message = data.message;
                $rootScope.sess = null;
            }
        });
};
  //login call to webapi (node implemented service)
    $scope.register = function(){
console.log($scope.user);
        $http.post('/auth/signup', $scope.user).success(function(data){
if(data.state == 'success'){
                $rootScope.authenticated = true;
                $rootScope.current_user = data.user.username;
                $location.path('/');
            }
            else{
                $scope.error_message = data.message;
            }
        });
    };
});
C++
//for main controller you can define any thing to make one way binding in main view.
main.controller("MainController", function ($scope) {}); //left empty, if not necessary don't create.

Lets create views that will render, Create New Folder Views and in the main directory add the following ejs code as ejs is the render engine used by the nodejs in Starter.ejs file.

C++
//<!DOCTYPE html>
<html ng-app="main">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="viewport" content="width=device-width" />
    <title>Super Application</title>
    <link href="bootstrap.css" rel="stylesheet" />
    <link href="Site.css" rel="stylesheet" />
    <script src="modernizr-2.6.2.js"></script>
    <script src="jquery-1.10.2.js"></script>
    <script src="bootstrap.js"></script>
    <script src="angular.js"></script>
    <script src="angular-route.js"></script>
    <script src="angular-ui-router.js"></script>
    <script src="angular-resource.js"></script>
    <script src="/Modules/mainApp.js"></script>
    <script src="/Controllers/MainController.js"></script>
    <script src="/Controllers/AuthController.js"></script>
</head>
<body>
    <div class="navbar navbar-inverse navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand" href="#/home">Application name</a>
            </div>
            
            <div class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li><a href="#/home">Home</a></li>
                    <li><a href="#/about">About</a></li>
                    <li><a href="#/contact">Contact</a></li>
                </ul>
                <ul class="nav navbar-nav navbar-right">
                    <li><p class="navbar-right navbar-text">Signed in as {{current_user}}</p></li>
                    <li><p class="navbar-right navbar-text" ng-hide="authenticated">
                        <a href="#/login">Login</a> or <a href="#/register">Register</a>
                    </p></li>
                    <li><p class="navbar-right navbar-text" ng-show="authenticated">
                        <a href="#" ng-click="signout()">Logout</a>
                    </p></li>
                </ul>
            </div>
        </div>
    </div>
    <div class="container body-content">
        <div ui-view>
        </div>
        <hr />
        <footer>
            <p>My ASP.NET Application</p>
        </footer>
    </div>
</body>
</html>

Now create new folder Authentication within Views folder, this folder will contain all the authentication views (register, login etc).

for login view add new file with name login.html in Authentication folder.

C++
<form class="form-auth" ng-submit="login()">
    <h2>Log In</h2>
    <p class="text-warning">{{error_message}}</p>
    <input type="username" ng-model="user.username" placeholder="Username" class="form-control" required><br>
    <input type="password" ng-model="user.password" placeholder="Password" class="form-control" required><br>
    <input type="submit" value="Log in" class="btn btn-primary" />
</form>

for register view add new file with name register.html in Authentication folder

C++
<form class="form-auth" ng-submit="register()">
    <h2>Register</h2>
    <p class="text-warning">{{error_message}}</p>
    <input type="email" ng-model="user.email" placeholder="Email" class="form-control" required><br>
    <input type="username" ng-model="user.username" placeholder="Username" class="form-control" required><br>
    <input type="password" ng-model="user.password" placeholder="Password" class="form-control" required><br>
    <select ng-init="user.role = roles[0].name" ng-model="user.role" ng-options="role.name as role.name for role in roles" class="form-control" required></select><br>
    <input type="submit" value="Sign Up" class="btn btn-primary" />
</form>

for unauth view add new file with name unauth.html in Authentication folder

C++
<form class="form-auth">
    <h2>You are Authentic/Unauthorize to access this page, This is because </h2>
    <p>1) Not login? Please register to access resources.</p>
    <p>2) Registered: You are not Authorize user, Please contact Administrator.</p>
</form>

Now create new folder Main within Views folder, this folder will contain all the main views (index, aboutus, contact etc).

for Index view add new file with name index.html in Main folder.

C++
 <div>
    <div class="jumbotron">
        <h1>Node.js Application</h1>
        <p class="lead">Node.js is a free Javascript framework for building great Web sites and Web applications using HTML, CSS and JavaScript.</p>
        <p><a class="btn btn-primary btn-lg">Learn more &raquo;</a></p>
    </div>
    <div class="row">
        <div class="col-md-4">
            <h2>Getting started</h2>
            <p>
                ASP.NET MVC gives you a powerful, patterns-based way to build dynamic websites that
                enables a clean separation of concerns and gives you full control over markup
                for enjoyable, agile development.
            </p>
            <p><a class="btn btn-default" href="http://go.microsoft.com/fwlink/?LinkId=301865">Learn more &raquo;</a></p>
        </div>
        <div class="col-md-4">
            <h2>Get more libraries</h2>
            <p>NuGet is a free Visual Studio extension that makes it easy to add, remove, and update libraries and tools in Visual Studio projects.</p>
            <p><a class="btn btn-default" href="http://go.microsoft.com/fwlink/?LinkId=301866">Learn more &raquo;</a></p>
        </div>
        <div class="col-md-4">
            <h2>Web Hosting</h2>
            <p>You can easily find a web hosting company that offers the right mix of features and price for your applications.</p>
            <p><a class="btn btn-default" href="http://go.microsoft.com/fwlink/?LinkId=301867">Learn more &raquo;</a></p>
        </div>
    </div>
</div>

for Aboutus view add new file with name About.html in Main folder.

C++
<div>
    <h2>About Us</h2>
    <h3>Message</h3>
    <p>Use this area to provide additional information.</p>
</div>

for Index view add new file with name Contact.html in Main folder.

C++
<div>
    <h2>Contact Us</h2>
    <h3>Message</h3>
    <address>
        One Microsoft Way<br />
        Redmond, WA 98052-6399<br />
        <abbr title="Phone">P:</abbr>
        425.555.0100
    </address>
    <address>
        <strong>Support:</strong>   <a href="mailto:Support@example.com">Support@example.com</a><br />
        <strong>Marketing:</strong> <a href="mailto:Marketing@example.com">Marketing@example.com</a>
    </address>
</div>

We have now done with our angular design side.

Lets create your backend with node.js

Create new routes from authentication requests with name authentication.js in Routes folder.

C++
var express = require('express');
var router = express.Router();
module.exports = function(passport){
//sends successful login state back to view(angular)
    router.get('/success',function(req,res){
           res.send({state: 'success', user: req.user ? req.user: null});    
    });
//send failure login state back to view(angular)
    router.get('/failure',function(req,res){
res.send({state: 'failure',user:null,message:"Invalid username or password"});
    });
    //login requeset
router.post('/login',passport.authenticate('login',{
        successRedirect: '/auth/success',
        failureRedirect: '/auth/failure'
    }));
//signup request
    router.post('/signup', passport.authenticate('signup', {
        successRedirect: '/auth/success',
        failureRedirect: '/auth/failure'
    }));
//logout request
    router.get('/signout', function(req, res) {
        req.session.user = null;
        req.logout();
        res.redirect('/');
    });
return router;
}

Create router.js in the same folder.

C++
var express = require('express');
var router = express.Router();
var mongoose = require( 'mongoose' );

router.get('/',function(req,res,next){    
    res.render('Starter',{title:"Super App"});    
});

module.exports = router;

Create new folder with Name Passport and add a new file (API) Name passport-init.js and add the following code. Your authentication route will call this Authentication Api.

C++
var mongoose = require('mongoose');   
var User = mongoose.model('User');
var LocalStrategy   = require('passport-local').Strategy;
var bCrypt = require('bcrypt-nodejs');
module.exports = function(passport){
// Passport needs to be able to serialize and deserialize users to support persistent login sessions
    passport.serializeUser(function(user, done) {
        console.log('serializing user:',user.username);
        done(null, user._id);
    });
passport.deserializeUser(function(id, done) {
        User.findById(id, function(err, user) {
            console.log('deserializing user:',user.username);
            done(err, user);
        });
    });
passport.use('login', new LocalStrategy({
            passReqToCallback : true
        },
        function(req, username, password, done) { 
        // check in mongo if a user with username exists or not
            User.findOne({ 'username' :  username }, 
                function(err, user) {
                    // In case of any error, return using the done method
                    if (err)
                        return done(err);
                    // Username does not exist, log the error and redirect back
                    if (!user){
                        console.log('User Not Found with username '+username);
                        return done(null, false);                 
                    }
                    // User exists but wrong password, log the error 
                    if (!isValidPassword(user, password)){
                        console.log('Invalid Password');
                        return done(null, false); // redirect back to login page
                    }
                    // User and password both match, return user from done method
                    // which will be treated like success
                    return done(null, user);
                }
            );
        }
    ));
passport.use('signup', new LocalStrategy({
            passReqToCallback : true // allows us to pass back the entire request to the callback
        },
        function(req, username, password, done, email, role) {
// find a user in mongo with provided username
            User.findOne({ 'username' :  username }, function(err, user) {
                // In case of any error, return using the done method
                if (err){
                    console.log('Error in SignUp: '+ err);
                    return done(err);
                }
                // already exists
                if (user) {
                    console.log('User already exists with username: '+username);
                    return done(null, false);
                } else {
                    // if there is no user, create the user
                    var newUser = new User();
                    // set the user's local credentials
                    newUser.username = username;
                    newUser.password = createHash(password);
                    newUser.email = req.body.email;
                    newUser.role = req.body.role;
                    // save the user
                    newUser.save(function(err) {
                        if (err){
                            console.log('Error in Saving user: '+err);  
                            throw err;  
                        }
                        console.log(newUser.username + ' Registration succesful');    
                        return done(null, newUser);
                    });
                }
            });
        })
    );
var isValidPassword = function(user, password){
        return bCrypt.compareSync(password, user.password);
    };
    // Generates hash using bCrypt
    var createHash = function(password){
        return bCrypt.hashSync(password, bCrypt.genSaltSync(10), null);
    };
};

Now add a new file server.js at the root directory as it'll be our main starting file

C++
//server.js
//adding opensource modules to application 
var express = require('express'); //express 
var path = require('path'); //for refering physical files here
var logger = require('morgan'); 
var cookieParser = require('cookie-parser'); //for maintain sessions
var bodyParser = require('body-parser'); //for parsing json
var bcrypt = require('bcrypt-nodejs'); 
var passport = require('passport'); //Using passportjs for authentication
var LocalStrategy = require('passport-local').Strategy; //using passport strategy
var session = require('express-session'); //for maintaining sessions
var mongoose = require('mongoose'); //for mongodb, database
var models_user = require('./Angular/Models/user.js'); // refering models in server.js

//connection database
mongoose.connect('mongodb://localhost/AngularizeApp');

//import the routers
var router = require('./Routes/router');
var authenticate = require('./Routes/authentication')(passport);

//for using express throughout this application
var app = express();

//tell node that My application will use ejs engine for rendering, view engine setup
app.set('views', path.join(__dirname, 'Views'));
app.set('view engine', 'ejs');

//tell node the global configuration about parser,logger and passport
app.use(cookieParser());
app.use(logger('dev'));
app.use(session({
  secret: 'keyboard cat'
}));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(passport.initialize()); //initializing passport
app.use(passport.session()); //initializing passport session

//tell node about these directories that application may get resources from
app.use('/', router);
app.use('/auth', authenticate);
app.use(express.static(path.join(__dirname, 'scripts')));
app.use(express.static(path.join(__dirname, 'Content')));
app.use(express.static(path.join(__dirname, 'Angular')));
app.use(express.static(path.join(__dirname, 'Views/Main')));
app.use(express.static(path.join(__dirname, 'Views/Authentication')));

//providing auth-api to passport so that it can use it.
var initPassport = require('./Passport/passport-init');
initPassport(passport);

//running server on node
var server = app.listen(3000, function () {
  var host = server.address().address;
  var port = server.address().port;
  console.log('Example app listening at http://%s:%s', host, port);
});

//exporting this application as a module
module.exports = app;

Now we have done with our code, You can run this application using node server.js command in cmd, by first directing it to the MEANApp root folder using cd c://MeanApp etc.

You also need to run the mongodb server for this open a new instace of cmd and type mongod -dbpath . if the mongodb installed and variable name added this command will run your mongodb server successfully. keep that cmd opened as if you close that instance of cmd the server will also close.

mongod -dbpath .

 

after running the above command now you can check your database in cmd, for this open another instance of cmd and type mongodb and you can see the database client in connected and you can switch to your required database. You can learn about mongodb in documentation provided here.

You can run the application by following command

MeanApp>node server.js

Once you logged in you can check the calls accordingly

 

Now we will be able to view the application running with authentication and contain session for storing data on the server side.

Following tutorial having all the functionality provided by Yeomen Scaffolding, however this tutorial is basically for learning that how the things are get done with understanding.

You can get the code from github here.

GitHub MEAN Stack Authentication Example

You can also download code from here.

Download Code Direct

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)