Tutorial 4: 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 4: 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 fourth 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 covered the `config` directory and how it works, including all configuration modules in the directory.

In this tutorial I would be working on the `API` for the application modules and discussing how each application feature interacts with the database.

The application has four main `API` modules as listed below

- Catalog API
- Order API
- Product API
- User API

Each `API` listed above includes the following module helping to perform the various required tasks.

- Model
- Controller
- Events
- Integration
- Socket

In this tutorial I will cover the `catalog`  while the remaining will be treated in a later tutorial.



### 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 house all the API code, there is a directory `api` which is a direct child of the `server` directory where all the code will be added.

#### Catalog

In the `api` directory, a sub-directory `catalog` will contain all the code for the `catalog` API.

The first covered module in the `catalog` directory is the catalog model to be used in the database.

**catalog.model.js**

In the `catalog.model.js` file we would add the database model for the catalog feature of the application.

Code

```
'use strict';

var  mongoose  =  require('bluebird').promisifyAll(require('mongoose'));
var  Schema  =  mongoose.Schema;
var  slugs  =  require('mongoose-url-slugs');

  

var  CatalogSchema  =  new  Schema({
	name: { type:  String, required:  true},
	parent: { type:  Schema.Types.ObjectId, ref:  'Catalog' },
	ancestors: [{ type:  Schema.Types.ObjectId, ref:  'Catalog' }],
	children: [{ type:  Schema.Types.ObjectId, ref:  'Catalog' }]
});

CatalogSchema.methods  = {
	addChild:  function (child) {
		var  that  =  this;
		child.parent  =  this._id;
		child.ancestors  =  this.ancestors.concat([this._id]);
		return  this.model('Catalog').create(child).addCallback(function (child) {
			that.children.push(child._id);
			that.save();
		});
	}
}

CatalogSchema.plugin(slugs('name'));  

module.exports  =  mongoose.model('Catalog', CatalogSchema);
```

`Bluebird` is set as a requirement alongside `mongoose` to help in the handling of Promise in the `api`.

Another dependency used in this file is the `mongoose-url-slugs` which allows us to generate and customize URL slugs.

The variable `CatalogSchema`generates a new object `Schema` which serves as the actual database model for the `catalog`.

`name` object will store the `catalog` name for each `Catalog`. It has a data type of `String` and the property `required: true` means that this document object is required in the creation of a catalog document.

`parent` will store the names of the `catalog` parent for each `Catalog`. It has a data type of `ObjectId` which was set through `type:  Schema.Types.ObjectId`.

`ancestors` is an array of objects that will store the names of the `catalog` ancestors for each `Catalog`. Like `parent` it has a data type of `ObjectId` which was set through `type:  Schema.Types.ObjectId`.

`children` is an array of objects that will store the names of the `catalog` children for each `Catalog`. Like `parent` and `ancestors` it has a data type of `ObjectId` which was set through `type:  Schema.Types.ObjectId`.

The `CatalogSchema` object has a method `addChild()` which dictates how the catalog `parent`, `child` and `ancestors` relate with each other when adding a new `child` catalog.


#### catalog.controller.js

In this file the code for the catalog controller module will be added.

The contents of this file will contain code that accepts requests from the view and passes it to the database for execution and also listens for a response to be returned and rendered in the view.

Code

```
/**
* Using Rails-like standard naming convention for endpoints.
* GET /api/catalogs -> index
* POST /api/catalogs -> create
* GET /api/catalogs/:id -> show
* PUT /api/catalogs/:id -> update
* DELETE /api/catalogs/:id -> destroy
*/

'use strict';

var  _  =  require('lodash');
var  Catalog  =  require('./catalog.model');

function  handleError(res, statusCode) {
	statusCode  =  statusCode  ||  500;
	return  function(err) {
		res.status(statusCode).send(err);
	};
}

function  responseWithResult(res, statusCode) {
	statusCode  =  statusCode  ||  200;
	return  function(entity) {
		if (entity) {
			res.status(statusCode).json(entity);
		}
	};
}

  

function  handleEntityNotFound(res) {
	return  function(entity) {
		if (!entity) {
			res.status(404).end();
			return  null;
		}
		return  entity;
	};
}

function  saveUpdates(updates) {
	return  function(entity) {
		var  updated  =  _.merge(entity, updates);
		return  updated.saveAsync()
			.spread(function(updated) {
				return  updated;
			});
	};
}

  

function  removeEntity(res) {
	return  function(entity) {
		if (entity) {
			return  entity.removeAsync()
				.then(function() {
					res.status(204).end();
				});
		}
	};
}

// Gets a list of Catalogs

exports.index  =  function(req, res) {
	Catalog.find().sort({_id:  1}).execAsync()
		.then(responseWithResult(res))
		.catch(handleError(res));
};

// Gets a single Catalog from the DB

exports.show  =  function(req, res) {
	Catalog.findByIdAsync(req.params.id)
		.then(handleEntityNotFound(res))
		.then(responseWithResult(res))
		.catch(handleError(res));
};

// Creates a new Catalog in the DB

exports.create  =  function(req, res) {
	Catalog.createAsync(req.body)
		.then(responseWithResult(res, 201))
		.catch(handleError(res));
};

  

// Updates an existing Catalog in the DB

exports.update  =  function(req, res) {
	if (req.body._id) {
		delete  req.body._id;
	}
	Catalog.findByIdAsync(req.params.id)
		.then(handleEntityNotFound(res))
		.then(saveUpdates(req.body))
		.then(responseWithResult(res))
		.catch(handleError(res));
};

// Deletes a Catalog from the DB

exports.destroy  =  function(req, res) {
	Catalog.findByIdAsync(req.params.id)
		.then(handleEntityNotFound(res))
		.then(removeEntity(res))
		.catch(handleError(res));
};
```

The `catalog` controller has two two required modules which includes the installed `lodash` dependency and the `catalog.model.js` module.

The controller also has a number of functions that handles the different states of the catalogs during operations.

`handleError()` is called whenever executions relating to the catalog module encounters an error.

The function returns the status code of the error being encountered or a `500` status code.

`responseWithResult()` checks for the presence of a variable `entity`. If `entity` exists the function returns a `200` status code with a `json` version of `entity`.

`handleEntityNotFound()` also checks for the presence of `entity`. Only this time, if `entity` is not found the function returns a `404` status code, else the function just returns `entity`.

`saveUpdates()` accepts new values from `updates` and merges them with existing values in `entity`.

The function then proceeds to save the newly merged values to the database and returns the value of the newly merged values in `updated`.

`removeEntity()` first of all checks for the existence of `entity`, if it exists the function returns a method `removeAsync()` which removes `entity` from the database.

`exports.index` is an exported function that looks through the database and returns a list of documents in the `Catalog` collection.

`exports.show`  is an exported function that looks through the database and returns the single `Catalog` document with an `id` matching the one provided in `req.params.id`.

`exports.create` is an exported function that will create a new document in the `Catalog` collection.

`exports.update` is an exported function that will update an existing document in the `Catalog` collection with data provided `req.body`.

`exports.destroy` is an exported function that will look for a document in the `Catalog` collection matching the one in `req.params.id` and remove it from the database.

All exported functions in this file makes use of the other local functions to handle `errors`, `updates`, `deletions` and `creations` in the `Catalog` database collection.

#### catalog.events.js

In this file we'll add/register an event emitter to the events in the `Catalog` model.

```
/**
* Catalog model events
*/

'use strict';

var  EventEmitter  =  require('events').EventEmitter;
var  Catalog  =  require('./catalog.model');
var  CatalogEvents  =  new  EventEmitter();

// Set max event listeners (0 == unlimited)
CatalogEvents.setMaxListeners(0);

// Model events
var  events  = {
	'save':  'save',
	'remove':  'remove'
};

// Register the event emitter to the model events
for (var  e  in  events) {
	var  event  =  events[e];
	Catalog.schema.post(e, emitEvent(event));
}

function  emitEvent(event) {
	return  function(doc) {
		CatalogEvents.emit(event  +  ':'  +  doc._id, doc);
		CatalogEvents.emit(event, doc);
	}
}

module.exports  =  CatalogEvents;
``` 

In this file the `events` module and `Catalog` model are set as requirements to be used during operation.

The line `CatalogEvents.setMaxListeners(0);` will set the number of event listeners that can be set up to unlimited.

The model events to listen for are stored in an object `events` highlighted in the code block below

```
var  events  = {
	'save':  'save',
	'remove':  'remove'
};
```

Event listeners are going to be on the lookout for any `save` or `remove` events in the `Catalog`.

The following code block will help register the event emitter to the model events

```
for (var  e  in  events) {
	var  event  =  events[e];
	Catalog.schema.post(e, emitEvent(event));
}
```

`events` is an array and for event `e` in the events array the block above posts `e` while also calling the `emitEvent()` function on `event`.

`emitEvent()` returns a function which emit the events stored in `CatalogEvents` using the `emit` method.


#### catalog.socket.js

In this module the listeners responsible for listening to events emitted in `catalog.events.js` are set up.

Code

```
/**
* Broadcast updates to client when the model changes
*/

'use strict';

var  CatalogEvents  =  require('./catalog.events');

// Model events to emit
var  events  = ['save', 'remove'];

exports.register  =  function(socket) {
	// Bind model events to socket events
	for (var  i  =  0, eventsLength  =  events.length; i  <  eventsLength; i++) {
		var  event  =  events[i];
		var  listener  =  createListener('catalog:'  +  event, socket);

		CatalogEvents.on(event, listener);
		socket.on('disconnect', removeListener(event, listener));
	}
};

function  createListener(event, socket) {
	return  function(doc) {
		socket.emit(event, doc);
	};
}

function  removeListener(event, listener) {
	return  function() {
		CatalogEvents.removeListener(event, listener);	
	};
}
```

`catalog.events.js` is set as a requirement in this file. 

The module stores a list of events to emit in the array `events` as shown below

```
var  events  = ['save', 'remove'];
```

For each event emitted in the events module we are going to create a listener that will listen and handle the event. The block of code below will handle the creation and removal of thee event listener.\

```
exports.register  =  function(socket) {
	// Bind model events to socket events
	for (var  i  =  0, eventsLength  =  events.length; i  <  eventsLength; i++) {
		var  event  =  events[i];
		var  listener  =  createListener('catalog:'  +  event, socket);

		CatalogEvents.on(event, listener);
		socket.on('disconnect', removeListener(event, listener));
	}
};
```

For each event whose index is less than the total number of events. The `createListener()` function is stored in a variable `listener` and is passed the parameters `event` and `socket` which will create a listener for the specific event happening.

The line `CatalogEvents.on(event, listener)` means that whenever an event occurs the callback function in `listener` should run.

When `socket` disconnects from the server, the `removeListener()` function runs which removes the created event listener from the module.

#### index.js

In the `index.js` file we are going to use the `express router` module to get the exported functions the `catalog` controller so they can be used other parts of the application.

Code

```
'use strict';

var  express  =  require('express');
var  controller  =  require('./catalog.controller');

var  router  =  express.Router();

router.get('/', controller.index);
router.get('/:id', controller.show);
router.post('/', controller.create);
router.put('/:id', controller.update);
router.patch('/:id', controller.update);
router.delete('/:id', controller.destroy);
module.exports  =  router;
```
In the code above the `express` module is set as a requirement. and the `express router` is gotten through the line `var  router  =  express.Router();`.

Using inbuilt router methods, this module creates, reads, updates and deletes catalogs from the `catalog` controller.

 The entire `Catalog` module is useful and vital in the creation of product categories and sub categories in the application.

In the next tutorial we will work on the `order` API which handles product orders in the store.


### 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)
2. [Building An Ecommerce Platform With The MEAN Stack - 2](https://steemit.com/utopian-io/@gotgame/tutorial-2-building-an-ecommerce-platform-with-the-mean-stack)

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



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