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.
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();
};
});
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
var mongoose = require('mongoose'); 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.
main.controller("AuthController", function ($scope, $http, $rootScope, $location) {
$scope.user = {username: '', password: ''};
$scope.error_message = '';
$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;
}
});
};
$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;
}
});
};
});
main.controller("MainController", function ($scope) {});
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.
<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.
<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
<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
<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.
<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 »</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 »</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 »</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 »</a></p>
</div>
</div>
</div>
for Aboutus view add new file with name About.html in Main folder.
<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.
<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.
var express = require('express');
var router = express.Router();
module.exports = function(passport){
router.get('/success',function(req,res){
res.send({state: 'success', user: req.user ? req.user: null});
});
router.get('/failure',function(req,res){
res.send({state: 'failure',user:null,message:"Invalid username or password"});
});
router.post('/login',passport.authenticate('login',{
successRedirect: '/auth/success',
failureRedirect: '/auth/failure'
}));
router.post('/signup', passport.authenticate('signup', {
successRedirect: '/auth/success',
failureRedirect: '/auth/failure'
}));
router.get('/signout', function(req, res) {
req.session.user = null;
req.logout();
res.redirect('/');
});
return router;
}
Create router.js in the same folder.
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.
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.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) {
User.findOne({ 'username' : username },
function(err, user) {
if (err)
return done(err);
if (!user){
console.log('User Not Found with username '+username);
return done(null, false);
}
if (!isValidPassword(user, password)){
console.log('Invalid Password');
return done(null, false); }
return done(null, user);
}
);
}
));
passport.use('signup', new LocalStrategy({
passReqToCallback : true },
function(req, username, password, done, email, role) {
User.findOne({ 'username' : username }, function(err, user) {
if (err){
console.log('Error in SignUp: '+ err);
return done(err);
}
if (user) {
console.log('User already exists with username: '+username);
return done(null, false);
} else {
var newUser = new User();
newUser.username = username;
newUser.password = createHash(password);
newUser.email = req.body.email;
newUser.role = req.body.role;
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);
};
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
var express = require('express'); var path = require('path'); var logger = require('morgan');
var cookieParser = require('cookie-parser'); var bodyParser = require('body-parser'); var bcrypt = require('bcrypt-nodejs');
var passport = require('passport'); var LocalStrategy = require('passport-local').Strategy; var session = require('express-session'); var mongoose = require('mongoose'); var models_user = require('./Angular/Models/user.js');
mongoose.connect('mongodb://localhost/AngularizeApp');
var router = require('./Routes/router');
var authenticate = require('./Routes/authentication')(passport);
var app = express();
app.set('views', path.join(__dirname, 'Views'));
app.set('view engine', 'ejs');
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()); app.use(passport.session());
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')));
var initPassport = require('./Passport/passport-init');
initPassport(passport);
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);
});
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