https://github.com/nodejs/node
In this new tutorial series I will be 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 this tutorial I will show you how the backend works and introduce you to to the general series.
By the end of this tutorial we would finished setting up the application server, routes.
Obviously there would be need for a project folder that will contain our application so you can create a project folder and name it anything you wish.
Inside the project folder there would be a folder that will contain all backend codes.
I named my backend directory server. ``The server directory is a direct child of the project directory.
The first thing to do here in the backend development phase is to install all dependencies and save them in the package.json file.
To install all dependencies correctly, firstly you need to navigate to the project directory.
In the project directory you can then run the command npm init which creates a package.json file where the list of dependencies can be stored.
The following are the contents of the package.json file which includes the list of dependencies needed in this application.
{
"name": "meanshop",
"version": "0.0.0",
"main": "server/app.js",
"dependencies": {
"babel-core": "^5.6.4",
"bluebird": "^2.9.34",
"body-parser": "~1.5.0",
"braintree": "^1.29.0",
"composable-middleware": "^0.3.0",
"compression": "~1.0.1",
"connect-mongo": "^0.8.1",
"connect-multiparty": "^2.0.0",
"cookie-parser": "~1.0.1",
"ejs": "~0.8.4",
"errorhandler": "~1.0.0",
"express": "~4.9.0",
"express-jwt": "^3.0.0",
"express-session": "~1.0.2",
"jsonwebtoken": "^5.0.0",
"lodash": "~2.4.1",
"method-override": "~1.0.0",
"mongoose": "^4.1.2",
"mongoose-url-slugs": "=0.1.4",
"morgan": "~1.0.0",
"passport": "~0.2.0",
"passport-facebook": "latest",
"passport-google-oauth": "latest",
"passport-local": "~0.1.6",
"passport-twitter": "latest",
"serve-favicon": "~2.0.1",
"socket.io": "^1.3.5",
"socket.io-client": "^1.3.5",
"socketio-jwt": "^4.2.0"
},
"devDependencies": {
"autoprefixer-core": "^5.2.1",
"bower": "^1.7.9",
"chai-as-promised": "^5.1.0",
"chai-things": "^0.2.0",
"connect-livereload": "^0.5.3",
"grunt": "~0.4.5",
"grunt-angular-templates": "^0.5.4",
"grunt-babel": "~5.0.0",
"grunt-build-control": "^0.5.0",
"grunt-concurrent": "^2.0.1",
"grunt-contrib-clean": "^0.6.0",
"grunt-contrib-concat": "^0.5.1",
"grunt-contrib-copy": "^0.8.0",
"grunt-contrib-cssmin": "^0.13.0",
"grunt-contrib-imagemin": "=0.9.1",
"grunt-contrib-jshint": "~0.11.2",
"grunt-contrib-sass": "^0.9.0",
"grunt-contrib-uglify": "^0.9.1",
"grunt-contrib-watch": "~0.6.1",
"grunt-dom-munger": "^3.4.0",
"grunt-env": "~0.4.1",
"grunt-express-server": "^0.4.17",
"grunt-filerev": "^2.3.1",
"grunt-google-cdn": "~0.4.0",
"grunt-injector": "^0.6.0",
"grunt-jscs": "^2.0.0",
"grunt-karma": "~0.12.0",
"grunt-mocha-istanbul": "^3.0.1",
"grunt-mocha-test": "~0.12.7",
"grunt-newer": "^1.1.1",
"grunt-ng-annotate": "^1.0.1",
"grunt-nodemon": "^0.4.0",
"grunt-open": "~0.2.3",
"grunt-postcss": "^0.5.5",
"grunt-protractor-runner": "^2.0.0",
"grunt-usemin": "^3.0.0",
"grunt-wiredep": "^2.0.0",
"istanbul": "^0.3.17",
"jit-grunt": "^0.9.1",
"jshint-stylish": "~2.0.1",
"karma": "~0.13.3",
"karma-babel-preprocessor": "^5.2.1",
"karma-chai-plugins": "^0.6.0",
"karma-chrome-launcher": "~0.2.0",
"karma-coffee-preprocessor": "~0.3.0",
"karma-firefox-launcher": "~0.1.6",
"karma-html2js-preprocessor": "~0.1.0",
"karma-jade-preprocessor": "0.0.11",
"karma-mocha": "^0.2.0",
"karma-ng-html2js-preprocessor": "~0.1.2",
"karma-ng-jade2js-preprocessor": "^0.2.0",
"karma-ng-scenario": "~0.1.0",
"karma-phantomjs-launcher": "~0.2.0",
"karma-requirejs": "~0.2.2",
"karma-script-launcher": "~0.1.0",
"karma-spec-reporter": "~0.0.20",
"mocha": "^2.2.5",
"open": "~0.0.4",
"proxyquire": "^1.0.1",
"requirejs": "~2.1.11",
"sinon-chai": "^2.8.0",
"supertest": "~0.11.0",
"time-grunt": "^1.2.1"
},
"engines": {
"node": ">=0.12.0"
},
"scripts": {
"start": "node server",
"test": "grunt test",
"postinstall": "bower install",
"update-webdriver": "node node_modules/grunt-protractor-runner/node_modules/protractor/bin/webdriver-manager update"
},
"private": true
}
In the package.json file there's a JSON object with key value pairs that contains some information about the application.
We have the name and version of the application.
We also have the main which points to the path of the main file for the application, in this case "server/app.js" which points to the app.js file in the server directory.
After adding package.json and installing all dependencies the time has come to include the main backend codes in the main backend file.
In the server directory I added the app.js file which serves as the main backend file containing the most basic of the backend configurations.
The code in the app.js file
/**
* Main application file
*/
'use strict';
// Set default node environment to development
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
var express = require('express');
var mongoose = require('mongoose');
var config = require('./config/environment');
// Connect to MongoDB
mongoose.connect(config.mongo.uri, config.mongo.options);
mongoose.connection.on('error', function(err) {
console.error('MongoDB connection to <'+ config.mongo.uri + '> failed: ' + err);
process.exit(-1);
});
// Populate databases with sample data
if (config.seedDB) { require('./config/seed'); }
// Setup server
var app = express();
var server = require('http').createServer(app);
var socketio = require('socket.io')(server, {
serveClient: config.env !== 'production',
path: '/socket.io-client'
});
require('./config/socketio')(socketio);
require('./config/express')(app);
require('./routes')(app);
// Start server
function startServer() {
server.listen(config.port, config.ip, function() {
if('test' !== app.get('env')) {
console.log('Express server listening on %s:%d, in %s mode', config.ip, config.port, app.get('env'));
}
});
}
setImmediate(startServer);
// Expose app
exports = module.exports = app;
The default node environment for the application to run is development . In order to set the default node environment I used the code below
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
In order to use some of the dependencies in the app.js they need to be imported and set as a requirement.
So the app.js file sets express, mongoose and the config/environment directory as requirements respectively using the following code
var express = require('express');
var mongoose = require('mongoose');
var config = require('./config/environment');
Using the imported mongoose package you can then connect the server to the MongoDB database. The code below handles the server-database connection operation
mongoose.connect(config.mongo.uri, config.mongo.options);
mongoose.connection.on('error', function(err) {
console.error('MongoDB connection to <'+ config.mongo.uri + '> failed: ' + err);
process.exit(-1);
});
mongoose.connect() will make an attempt to connect the url provided by config.mongo.uri
If mongoose.connect() encounters an error then the callback function in mongoose.connection.on() runs which returns a string indicating that the connection attempt encountered an error.
The database needs to hold some sample data that the application developer can use for testing purposes.
The sample data can be found in the seedDB module which is a file in the config directory which I will cover later.
In the app.js file I included the following line to check for the exostence of the seedDB module.
if (config.seedDB) { require('./config/seed'); }
If the module exists then the module is set as a requirement.
After populating the database with sample data you can now setup the server. The server is setup using the following
var app = express();
var server = require('http').createServer(app);
var socketio = require('socket.io')(server, {
serveClient: config.env !== 'production',
path: '/socket.io-client'
});
require('./config/socketio')(socketio);
require('./config/express')(app);
require('./routes')(app);
On one hand express() is set as a requirement.
The browser http module is also set as a requirement and used as a threshold to access the createServer() method which helps in the creation of a new server.
To enable real time communication in the application the package socket.io is required. socket.io is then set as a requirement with an additional method that configures the module for use.
Three other required modules are then imported for use in the app.js file.
The first two are from the config directory and they are the configuration files for socketio and expressrespectively.
The last is the routes file which holds all the routes used in the application.
Starting the server and getting it to run will be handled by the function startServer().
The function tells the server to listen to anything from the port or ip stored in the config.
If the server listens successfully, the callback function runs. The callback function checks if the present environment is not the test environment.
If that condition is true a string indicating that the server is successfully listening is logged in the console.
And finally I export the module in the app.js file through the line
exports = module.exports = app;
In the same directory as app.js I created a new file named index.js.
The work index.js does is simple and straight forward, it helps register the babel-core/register module through the require hook.
The code for index.js
'use strict';
// Register the Babel require hook
require('babel-core/register');
// Export the application
exports = module.exports = require('./app');
In the app.js file we set the routes.js file as a requirement using the require hook.
This is because routes.js is where all the routes in the application are registered.
routes.js code
/**
* Main application routes
*/
'use strict';
var errors = require('./components/errors');
var path = require('path');
module.exports = function(app) {
// Insert routes below
app.use('/api/catalogs', require('./api/catalog'));
app.use('/api/braintree', require('./api/braintree'));
app.use('/api/orders', require('./api/order'));
app.use('/api/products', require('./api/product'));
app.use('/api/users', require('./api/user'));
app.use('/auth', require('./auth'));
// All undefined asset or api routes should return a 404
app.route('/:url(api|auth|components|app|bower_components|assets)/*')
.get(errors[404]);
// All other routes should redirect to the index.html
app.route('/*')
.get(function(req, res) {
res.sendFile(path.resolve(app.get('appPath') + '/index.html'));
});
};
In the file we first of all register two module
var errors = require('./components/errors');
var path = require('path');
The first module is gotten from the components folder in the server directory indicated by the path /components/errors.
The module handle all the errors that will arise when running the application.
The second module path is a universal package that come pre-installed in every machine.
The pathmodule handle the routing paths for the application features.
The variable/directive module.exports contains a function that helps setup all routes and gets them ready for export to be used in other modules.
In the function we setup all routes for use through the following blocks
app.use('/api/catalogs', require('./api/catalog'));
app.use('/api/braintree', require('./api/braintree'));
app.use('/api/orders', require('./api/order'));
app.use('/api/products', require('./api/product'));
app.use('/api/users', require('./api/user'));
app.use('/auth', require('./auth'));
The method app.use() tells the application server(app.js file) to make use of the of the routes in the brackets.
We have the following routes in the application,
api/catalogs which handles the store/product catalogs.
api/braintree which handles all relating to the braintree authentication module.
api/orders which handles the orders from the store.
api/products which handles all products in the store.
api/users for user management.
auth for user authentication in the application.
Whenever a user requests a url that doesn't exist in the registered routes the application returns a 404 status code indicating that the route/url wasn't found.
The following line of code handles the 404 rendering in the application
app.route('/:url(api|auth|components|app|bower_components|assets)/*')
.get(errors[404]);
In the next tutorial I will continue work on the backend by sharing the database configurations used in the application.
Building A Content Management System Using The MEAN Stack - 2(Create Controller Modules 1)
Building A Content Management System Using The MEAN Stack - 3 (Create Controller Modules 2)
Building A Content Management System Using The MEAN Stack - 4 (Create Services Modules)
Building A Content Management System Using The MEAN Stack - 5 (Front-End Development)
Building A Content Management System Using The MEAN Stack - 6 (Front-End Development)
Building A Content Management System Using The MEAN Stack - 7 (Front-End Development)
Building A Content Management System Using The MEAN Stack - 8 (Front-End Development)
Building A Content Management System Using The MEAN Stack - 9 (Front-End Development)
Building A Content Management System Using The MEAN Stack - 10 (Front-End Development)