Tutorial 2: Building An Ecommerce Platform With The MEAN Stack

in #utopian-io6 years ago (edited)

Repository

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

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

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 lineapp.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:  '[email protected]',
            password:  'test'
        }, {
            provider:  'local',
            role:  'admin',
            name:  'Admin',
            email:  '[email protected]',
            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:  '[email protected]',
    password:  'test'
}, {
    provider:  'local',
    role:  'admin',
    name:  'Admin',
    email:  '[email protected]',
    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

Proof Of Work Done

https://github.com/olatundeee/meanshop

Sort:  

Thank you for your contribution @gotgame.
After analyzing your tutorial we suggest the following points below:

  • If you do a very extensive tutorial it is important to put some images of what you are explaining and developing to break some of your text.

Your contribution has been evaluated according to Utopian policies and guidelines, as well as a predefined set of questions pertaining to the category.

To view those questions and the relevant answers related to your post, click here.


Need help? Write a ticket on https://support.utopian.io/.
Chat with us on Discord.
[utopian-moderator]

Thank you for your review, @portugalcoin! Keep up the good work!

Hello! Your post has been resteemed and upvoted by @ilovecoding because we love coding! Keep up good work! Consider upvoting this comment to support the @ilovecoding and increase your future rewards! ^_^ Steem On!

Reply !stop to disable the comment. Thanks!

Hi @gotgame!

Your post was upvoted by @steem-ua, new Steem dApp, using UserAuthority for algorithmic post curation!
Your post is eligible for our upvote, thanks to our collaboration with @utopian-io!
Feel free to join our @steem-ua Discord server

Hi, @gotgame!

You just got a 0.68% upvote from SteemPlus!
To get higher upvotes, earn more SteemPlus Points (SPP). On your Steemit wallet, check your SPP balance and click on "How to earn SPP?" to find out all the ways to earn.
If you're not using SteemPlus yet, please check our last posts in here to see the many ways in which SteemPlus can improve your Steem experience on Steemit and Busy.

Hey, @gotgame!

Thanks for contributing on Utopian.
We’re already looking forward to your next contribution!

Get higher incentives and support Utopian.io!
Simply set @utopian.pay as a 5% (or higher) payout beneficiary on your contribution post (via SteemPlus or Steeditor).

Want to chat? Join us on Discord https://discord.gg/h52nFrV.

Vote for Utopian Witness!

Coin Marketplace

STEEM 0.18
TRX 0.15
JST 0.028
BTC 63597.74
ETH 2476.06
USDT 1.00
SBD 2.53