MEAN Stack is a combination of the following components:
MongoDB (Document database) – Storage and retrieval of data
Express (Back-end application framework) – Makes requests to Database for Reads and Writes.
Angular (Front-end application framework) – Handles Client and Server Requests
NodeJS (JavaScript runtime environment) – Accepts requests and displays results to end user
To integrate the above technologies let's build a book register.
NodeJS Setup
sudo apt update -y
curl -fsSL https://deb.nodesource.com/setup_current.x | sudo -E bash -
sudo apt update -y # might be necessary to update source index
sudo apt install -y nodejs
After installing NodeJS let's create the project directory, run npm init
in it and create the folder structure:
sudo mkdir Book; cd Book; sudo npm init -y
We should now have a package.json
file inside of the Book
directory. At the end of the project we should have a folder structure like this:
├── Book
│ ├── apps
│ │ ├── routes.js
│ │ ├── models
│ │ │ ├── book.js
├── public
│ ├── index.js
│ ├── index.html
├── node_modules
├── server.js
├── package.json
└── package-lock.json
Express Setup
Express is a minimal and flexible Node.js
web application framework that provides features for web and mobile applications. We will use Express to pass book information to and from our MongoDB database.
sudo npm i express body-parser
// ~/Book/server.js
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(express.static(__dirname + '/public'));
app.use(bodyParser.json());
require('./apps/routes')(app);
app.set('port', 3300);
app.listen(app.get('port'), function() {
console.log('Server up: http://localhost:' + app.get('port'));
});
Additionally, we need to include port 3300
in our EC2 instance's security group to be able to access the express server.
MongoDB Setup
MongoDB stores data in flexible, JSON-like
documents. Fields in a database can vary from document to document and data structure can be changed over time. For our example application, we are adding book records to MongoDB that contain a book's name, ISBN, author, and number of pages.
Installation
Let's install the latest version of the MongoDB community edition. The commands below are pulled from the official docs:
wget -qO - https://www.mongodb.org/static/pgp/server-6.0.asc | sudo apt-key add -
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/6.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-6.0.list
sudo apt update
sudo apt install -y mongodb-org
sudo systemctl daemon-reload
sudo systemctl start mongod
We should see the service running now:
Mongoose Setup
We will use Mongoose to establish a schema for the database to store data of our book register. Let's create the schema for our book model with fields for name, ISBN, author, and pages:
// ~/Book/apps/models/book.js
const mongoose = require('mongoose');
const dbHost = 'mongodb://localhost:27017/test';
mongoose.connect(dbHost);
mongoose.connection;
mongoose.set('debug', true);
const bookSchema = mongoose.Schema({
name: String,
isbn: { type: String, index: true },
author: String,
pages: Number
});
const Book = mongoose.model('Book', bookSchema);
module.exports = mongoose.model('Book', bookSchema);
Express Routes
We need to define routes for which our express server should handle when a request is made to it. The following lists /book
route for requests for GET & POST, as well as a /book/:isbn
to delete a book record when a DELETE request is provided with an isbn
value for that book.
// ~/Book/apps/routes.js
const Book = require('./models/book');
module.exports = function(app) {
// Returns all books
app.get('/book', function(req, res) {
Book.find({}, function(err, result) {
if ( err ) throw err;
res.json(result);
});
});
app.post('/book', function(req, res) {
// Adds book to book collection
const { name, isbn, author, pages } = req.body;
const book = new Book({ name, isbn, author, pages });
book.save(function(err, result) {
if (err) throw err;
res.json( {
message:"Successfully added book",
book: result
});
});
});
app.delete("/book/:isbn", function(req, res) {
// Find book based on isbn value and remove from collection
Book.findOneAndRemove(req.query, function(err, result) {
if (err) throw err;
res.json( {
message: "Successfully deleted the book",
book: result
});
});
});
const path = require('path');
app.get('*', function(req, res) {
res.sendfile(path.join(__dirname + '/public', 'index.html'));
});
};
AngularJS Setup
AngularJS provides a web framework for creating dynamic views in your web applications. We need an index.html
to present our data and a index.js
to handle user input for adding or removing books from our records.
sudo npm i angularjs
<!-- ~/Book/public/index.html -->
<!doctype html>
<html ng-app="myApp" ng-controller="myCtrl">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<script src="index.js"></script>
</head>
<body>
<div>
<table>
<tr>
<td>Name:</td>
<td><input type="text" ng-model="Name"></td>
</tr>
<tr>
<td>Isbn:</td>
<td><input type="text" ng-model="Isbn"></td>
</tr>
<tr>
<td>Author:</td>
<td><input type="text" ng-model="Author"></td>
</tr>
<tr>
<td>Pages:</td>
<td><input type="number" ng-model="Pages"></td>
</tr>
</table>
<button ng-click="add_book()">Add</button>
</div>
<hr />
<div>
<table>
<tr>
<th>Name</th>
<th>Isbn</th>
<th>Author</th>
<th>Pages</th>
</tr>
<tr ng-repeat="book in books">
<td>{{book.name}}</td>
<td>{{book.isbn}}</td>
<td>{{book.author}}</td>
<td>{{book.pages}}</td>
<td><input type="button" value="Delete" data-ng-click="del_book(book)"></td>
</tr>
</table>
</div>
</body>
</html>
Now for the interactivity connection to the UI:
// ~/Book/public/script.js
const app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, $http) {
$http({
method: 'GET',
url: '/book'
}).then(function successCallback(response) {
$scope.books = response.data;
}, function errorCallback(response) {
console.log('Error: ' + response);
});
$scope.del_book = function(book) {
$http( {
method: 'DELETE',
url: '/book/:isbn',
params: { 'isbn': book.isbn }
}).then(function successCallback(response) {
console.log(response);
}, function errorCallback(response) {
console.log('Error: ' + response);
});
};
$scope.add_book = function() {
const body = {
name: $scope.Name,
isbn: $scope.Isbn,
author: $scope.Author,
pages: $scope.Pages,
};
$http({
method: 'POST',
url: '/book',
data: body
}).then(function successCallback(response) {
console.log(response);
}, function errorCallback(response) {
console.log('Error: ' + response);
});
};
});
Our app is now complete with simple CRD (Create, Read, Delete) functionality. Start the express server with node index.js
in the ~/Book
directory.
Opening the app on our public IP address on port 3300
(for ex: http://54.213.246.86:3300
) should display the below UI:
The below image shows the insertion of a book into the collection and a query to return all books: