Tutorial 2: Building An Ecommerce Platform With The MEAN Stack

View this thread on: d.buzz | hive.blog | peakd.com | ecency.com
·@gotgame·
0.000 HBD
Tutorial 2: Building An Ecommerce Platform With The MEAN Stack
### Repository

[https://github.com/nodejs/node](https://github.com/nodejs/node)

#### What Will I Learn

This is the second tutorial in the series where I am documenting and sharing the process of building an ecommerce application using MongoDB, Express, AngularJS and NodeJS.

The code for the entire series is based on the book **Web Application Development with MEAN** by [Adrian Mejia](https://adrianmejia.com/)

In the last tutorial I showed you how the backend works and did a general introduction to the series.

In this tutorial we would build our application a step further by working on the configurations relating to the database and some special modules/dependencies.

By the end of this tutorial we would have set up the application `config` directory and some files required with the folder.

### Requirements

-   [NodeJS and NPM](https://nodejs.org/en/download/package-manager/),
-   [Angular](https://angular.io/)
-   [MongoDB](https://www.mongodb.com/)
-   Code Editor

### Difficulty

-   Intermediate

### Tutorial Contents

In order to setup the other configurations for the application database and backend I created the folder `config` as the direct child of the `server` directory.

The `config` directory was somewhat mentioned in the first tutorial as part of the requirements needed in the `app.js` file.

In the `config` directory there is a number of files and one sub directory performing certain special functions.

#### express.js

The first configuration file in the `config` directory. The code contents of this file is responsible for connecting some backend dependencies with the database where they are required

In the `express.js` file we have the following code 

```
/**

* Express configuration

*/

'use strict';

var  express  =  require('express');
var  favicon  =  require('serve-favicon');
var  morgan  =  require('morgan');
var  compression  =  require('compression');
var  bodyParser  =  require('body-parser');
var  methodOverride  =  require('method-override');
var  cookieParser  =  require('cookie-parser');
var  errorHandler  =  require('errorhandler');
var  path  =  require('path');
var  config  =  require('./environment');
var  passport  =  require('passport');
var  session  =  require('express-session');
var  mongoStore  =  require('connect-mongo')(session);
var  mongoose  =  require('mongoose');

module.exports  =  function(app) {
	var  env  =  app.get('env');
	app.set('views', config.root  +  '/server/views');
	app.engine('html', require('ejs').renderFile);
	app.set('view engine', 'html');
	app.use(compression());
	app.use(bodyParser.urlencoded({ extended:  false }));
	app.use(bodyParser.json());
	app.use(methodOverride());
	app.use(cookieParser());
	app.use(passport.initialize());
	  
	// Persist sessions with mongoStore / sequelizeStore
	// We need to enable sessions for passport twitter because its an oauth 1.0 strategy
	app.use(session({
		secret:  config.secrets.session,
		resave:  true,
		saveUninitialized:  true,
		store:  new  mongoStore({
			mongooseConnection:  mongoose.connection,
			db:  'meanshop'
		})
	}));

	app.set('appPath', path.join(config.root, 'client'));

	if ('production'  ===  env) {
		app.use(favicon(path.join(config.root, 'client', 'favicon.ico')));
		app.use(express.static(app.get('appPath')));
		app.use(morgan('dev'));
	}

	if ('development'  ===  env  ||  'test'  ===  env) {
		app.use(require('connect-livereload')());
		app.use(express.static(path.join(config.root, '.tmp')));
		app.use(express.static(app.get('appPath')));
		if('test'  !==  env) app.use(morgan('dev'));
		app.use(errorHandler()); // Error handler - has to be last
	}
};
```
The require hook was utilized in this file to set all needed modules up for use.

After setting up the file dependencies a function is exported through `module.exports`. This function contains all the actual configurations for this file.

There are three environments in the application including `development`, `production` and `test`.

This line of code `var  env  =  app.get('env');` will help determine the present environment the application is running on.

The following block also sets further configurations relating to the front end, data parsing and some

```
app.set('views', config.root  +  '/server/views');
app.engine('html', require('ejs').renderFile);
app.set('view engine', 'html');
app.use(compression());
app.use(bodyParser.urlencoded({ extended:  false }));
app.use(bodyParser.json());
app.use(methodOverride());
app.use(cookieParser());
app.use(passport.initialize());
```

In the block above, the path to the `views` folder which will contain the `html` file to the `Not Found Error` feature is set to `'/server/views'` through the line `app.set('views', config.root + '/server/views');`.

`app.engine('html', require('ejs').renderFile);` is responsible for setting the `html` templating engine module `ejs` as a requirement.

The view engine is then set as `ejs` through the line`app.set('view engine', 'html');`

`app.use(compression());` will use the `compression` package in order to enable `gzip compression` in the application.

Other module set for use in the block include `bodyparser`, `method override`, `cookie parser` and `passport`.

The block below will help to enable sessions for passport twitter. 
```
app.use(session({
	secret:  config.secrets.session,
	resave:  true,
	saveUninitialized:  true,
	store:  new  mongoStore({
		mongooseConnection:  mongoose.connection,
		db:  'meanshop'
	})
}));
```

The block above uses the `express-session` and `connect-mongo` module to create a database connection to the `MongoDB` database

If the application is running on the production environment the following block executes

```
if ('production'  ===  env) {
	app.use(favicon(path.join(config.root, 'client', 'favicon.ico')));
	app.use(express.static(app.get('appPath')));
	app.use(morgan('dev'));
}
```

In the code above the path to the `favicon` is firstly set using the `path` module.

The static front end folder files is also set to the value of the `appPath` directive which was set earlier in the file.

In case the application is running on the `development` or the `test` environment the following configuration is set.

```
if ('development'  ===  env  ||  'test'  ===  env) {
	app.use(require('connect-livereload')());
	app.use(express.static(path.join(config.root, '.tmp')));
	app.use(express.static(app.get('appPath')));
	if('test'  !==  env) app.use(morgan('dev'));
	app.use(errorHandler()); // Error handler - has to be last
}
```
On the first line `connect-liverreload` is used as the middleware for adding the `liverreload` script.

Second and third line sets the front end folder using the `express.static()` method to to set the directory path.


#### locals.env.js

In the `locals.env.js` file, the admin/dev of the store can store the `api` keys to be used for authentication from different services.

The code 

```
'use strict';

// Use local.env.js for environment variables that grunt will set when the server starts locally.

// Use for your api keys, secrets, etc. This file should not be tracked by git.

//

// You will need to set these on the server you deploy to.

module.exports  = {
	DOMAIN:  'http://localhost:9000',
	SESSION_SECRET:  'meanshop-secret',

	FACEBOOK_ID:  'app-id',
	FACEBOOK_SECRET:  'secret',

	TWITTER_ID:  'app-id',
	TWITTER_SECRET:  'secret',

	GOOGLE_ID:  'app-id',
	GOOGLE_SECRET:  'secret',

	BRAINTREE_ID:  'public key',
	BRAINTREE_SECRET:  'private key',
	BRAINTREE_MERCHANT:  'merchant ID',

	// Control debug level for modules using visionmedia/debug
	DEBUG:  ''
};
```

Each key value pair represents the data needed from each service provider in order to be able use their service for authentication.

The services in the file includes `Facebook authentication`,  `Twitter Authentication`, `Google Authentication` and `Braintree Authentication`

#### seed.js

The `seed.js` file is responsible for helping to populate the database with sample data that will be used for application testing purposes.

Among the sample data included in the file we can find

- sample user data to enable the admin login to the store as admin and perform admin related operations

- sample products with different categories for populating the store

Code

```
/**
* Populate DB with sample data on server start
* to disable, edit config/environment/index.js, and set `seedDB: false`
*/

'use strict';

var  User  =  require('../api/user/user.model');
var  Product  =  require('../api/product/product.model');
var  Catalog  =  require('../api/catalog/catalog.model');
var  mainCatalog, home, books, clothing;

User.find({}).removeAsync()
	.then(function() {
		User.createAsync({
			provider:  'local',
			name:  'Test User',
			email:  'test@test.com',
			password:  'test'
		}, {
			provider:  'local',
			role:  'admin',
			name:  'Admin',
			email:  'admin@admin.com',
			password:  process.env.ADMIN_PASSWORD  ||  'admin'
		})
		.then(function() {
			console.log('finished populating users');
		});
	});

Catalog
	.find({})
	.remove()
	.then(function () {
		return  Catalog.create({ name:  'All'});
	})
	.then(function (catalog) {
		mainCatalog  =  catalog;
		return  mainCatalog.addChild({name:  'Home'});
	})
	.then(function (category) {
		home  =  category._id;
		return  mainCatalog.addChild({name:  'Books'});
	})
	.then(function (category) {
		books  =  category._id;
		return  mainCatalog.addChild({name:  'Clothing'});
	})
	.then(function (category) {
		clothing  =  category._id;
		return  Product.find({}).remove({});
	})
	.then(function() {
		return  Product.create({
			title:  'MEAN eCommerce Book',
			imageUrl:  '/assets/uploads/meanbook.jpg',
			price:  25,
			stock:  250,
			categories: [books],
			description:  'Build a powerful e-commerce application quickly with MEAN, a leading full-JavaScript stack. It takes you step-by-step from creating a real-world store to managing details such as authentication, shopping carts, payment, scalability and more.'
		}, {
			title:  'T-shirt',
			imageUrl:  '/assets/uploads/meantshirt.jpg',
			price:  15,
			stock:  100,
			categories: [clothing],
			description:  'T-shirt with the MEAN stack logo'
		}, {
			title:  'Coffee Mug',
			imageUrl:  '/assets/uploads/meanmug.jpg',
			price:  8,
			stock:  50,
			categories: [home],
			description:  'Convert coffee into MEAN code'
		});
	})
	.then(function () {
		console.log('Finished populating Products with categories');
	})
	.then(null, function (err) {
		console.error('Error populating Products & categories: ', err);
	});
```

If you do not wish to use the sample data located in the `seed.js` file just edit the `config/environment/index.js` file and set `seedDB: false`.

Each sample database collection to be created needs database model to be based on.

The code below sets the `User model`, `Product model` and `Catalog model` as requirements in the file since the sample to be populated cut across users and products

```
var  User  =  require('../api/user/user.model');
var  Product  =  require('../api/product/product.model');
var  Catalog  =  require('../api/catalog/catalog.model');
```

`User.find({}).removeAsync()` looks for the `User` collection in the database, if `User` does not exist the function creates a new `User` document.

After creating the user collection then the function goes on to populate it with the sample user data provided below

```
User.createAsync({
	provider:  'local',
	name:  'Test User',
	email:  'test@test.com',
	password:  'test'
}, {
	provider:  'local',
	role:  'admin',
	name:  'Admin',
	email:  'admin@admin.com',
	password:  process.env.ADMIN_PASSWORD  ||  'admin'
})
.then(function() {
	console.log('finished populating users');
});
```

Each created user has a `role`, `name`, `email` and `password` field attached to their documents.

We'll cover the `user model` indepth in the future. 

The catalog and products data are intertwined, the following code will create a catalog collection and add product categories to the catalog

```
Catalog
	.find({})
	.remove()
	.then(function () {
		return  Catalog.create({ name:  'All'});
	})
	.then(function (catalog) {
		mainCatalog  =  catalog;
		return  mainCatalog.addChild({name:  'Home'});
	})
	.then(function (category) {
		home  =  category._id;
		return  mainCatalog.addChild({name:  'Books'});
	})
	.then(function (category) {
		books  =  category._id;
		return  mainCatalog.addChild({name:  'Clothing'});
	})
	.then(function (category) {
		clothing  =  category._id;
		return  Product.find({}).remove({});
	})
```

In the block above two main categories are created which are `Books` and `Clothing`, in addition to the over all `Home` category which features all products.

After product categories are created, then the sample products are added through the block below

```
.then(function() {
	return  Product.create({
		title:  'MEAN eCommerce Book',
		imageUrl:  '/assets/uploads/meanbook.jpg',
		price:  25,
		stock:  250,
		categories: [books],
		description:  'Build a powerful e-commerce application quickly with MEAN, a leading full-JavaScript stack. It takes you step-by-step from creating a real-world store to managing details such as authentication, shopping carts, payment, scalability and more.'
	}, {
		title:  'T-shirt',
		imageUrl:  '/assets/uploads/meantshirt.jpg',
		price:  15,
		stock:  100,
		categories: [clothing],
		description:  'T-shirt with the MEAN stack logo'
	}, {
		title:  'Coffee Mug',
		imageUrl:  '/assets/uploads/meanmug.jpg',
		price:  8,
		stock:  50,
		categories: [home],
		description:  'Convert coffee into MEAN code'
	});
})
.then(function () {
	console.log('Finished populating Products with categories');
})
.then(null, function (err) {
	console.error('Error populating Products & categories: ', err);
});
```

Each created product document possesses the following properties

- Product title
- Product Image
- Product Price
- Product Stock
- Product categories
- Product description

If at the time of running the application the product is added successfully a message is logged to the console indicating the success of the additions

In the next tutorial I will complete the `config` aspect of the backend which includes the environment configurations for the different environments the application will run on at different periods.


### Curriculum

1.  [Building An Ecommerce Platform With The MEAN Stack - 1](https://steemit.com/utopian-io/@gotgame/tutorial-1-building-an-ecommerce-application-with-the-mean-stack)



### Proof Of Work Done  
https://github.com/olatundeee/meanshop
👍 , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,