Presented by Norberto Leite, Developer Advocate, MongoDB
Experience level: Advanced
Get ready to be MEAN! The MEAN Stack (MongoDB, ExpressJS, AngularJS and Node.js) allows developers to do rapid application development and application scaffolding. In this session, Norberto will walk you through strategies and best practices for building applications on the MEAN stack, the benefits of using such an application stack and the key benefits of each of the individual components.
5. 5
Building and MEAN app
• MongoDB is great for storing web/mobile app data
• So let’s build a REST API using Node.js!
– learn a bit about overall MEAN Stack libraries
–learn a few MongoDB schema design principles
7. 7
Overview
• Part 1: Shopping Cart Application
– Search for products
– Add them to your cart
– Check out with Stripe
• Part 2: Using the Mongoose ODM
• Part 3: Schema Design
• Part 4: Building an API with the Express framework
• Part 5: Front End w/ Angular
13. "Bad programmers worry about the code. Good
programmers worry about data structures and their
relationships." - Linus Torvalds
3 schemas for 3 collections
Products Categories Users
14. 16
Schema Relationships
Entity A Entity B
1 1
Document A
Document B
Entity A Entity B
1 N
Document A
Array of
B's
Entity A Entity B
1 NNNN
Entity A Entity B
N N
Document A Document B
16. 18
Schema Relationships
• Product belongs to one or more categories
• Users can have multiple products in their cart
• Representing relationships in MongoDB is tricky (
different from doing a traditional RDBMS system – based
on the usage patterns of data)
• But that’s what mongoose HELPS A LOT!
19. 21
Your first Mongoose Schema!
var mongoose = require('mongoose');
module.exports = new mongoose.Schema(
{
name:{
type: String,
required: true,
}
email:{
type: String,
required: true,
match: /.+@.+..+/,
lowercase: true
},
loggedInCounter:{
type: Number,
default: 0,
}
});
"require" mongoose
Define fields
Field types, validation rules
and attributes
20. Document Validation
Available on 3.2.0-RC2
https://jira.mongodb.org/browse/SERVER-18227
http://www.eliothorowitz.com/blog/2015/09/11/document-validation-and-what-dynamic-
schema-means/
21. 23
Using your schemavar mongoose = require('mongoose');
var schema = require('./schemas/user');
mongoose.connect('mongodb://localhost:27017/meancart');
var User = mongoose.Model('User', schema, 'users');
var newuser = new User({
name: 'Jose Mourinho',
email: 'specialone@chelseafc.co.uk',
});
newuser.save( function(error){
if (error){
console.log(error);
process.exit(1);
}
User.find({email: 'specialone@chelseafc.co.uk'}, function(error, docs){
if(error){
console.log(error);
process.exit(1);
}
console.log(require('util').inspect(docs));
process.exit(1);
});
} );
Tell mongoose where to
connect
Create model using given
schema and initialize object
Save user and load it from
MongoDB
22. 24
Takeaways
• Mongoose provides several neat features
– MVC model
–Default values
– Schema validation
–Declarative schema design
26. $lookup
Go and try it out w/ 3.2.0-RC2:
https://jira.mongodb.org/browse/SERVER-19095
https://www.mongodb.com/blog/post/thoughts-on-new-feature-lookup
28. 30
Creating a Productvar mongoose = require('mongoose');
var productSchema = require('./product');
var Product = mongoose.model('Product', productSchema);
var p = new Product({
name: 'chelsea scarf blue',
price: {
amount: 12.97,
currency: 'GBP',
},
category:{
name:'scarfs'
}
});
p.name = 2;
console.log(p.name); //2
console.log(p.$isValid('name')); // true
p.price.amount = 'Not a number';
console.log(p.$isValid('price.amount')); //false
p.validate(function(err){
// CastError because `price.amount` couldn't be
// casted to a number
console.log(err);
});
Cast Validation
Invalid Cast
Validation Method
29. 31
Category Schema Queries
• What categories are descent of "wearables" ?
• What categories are children of "accessories"?
• What categories are ancestors of "scarf"?
db.category.find( {'ancestors': 'wearables'})
{"_id": "wearables", "parent": "accessories", "ancestors": ["accessories","wearables"]}
{"_id": "scarfs", "parent": "wearables", "ancestors": ["accessories", "wearables", "scarfs"]}
db.category.find( {'parent': "accessories"})
{"_id": "wearables", "parent": "accessories", "ancestors": ["accessories","wearables"]}
db.category.find( {'_id': 'scarf'}, {"_id":0, "ancestors":1})
{"ancestors": ["accessories","wearables"]}
30. 32
… make sure that:
• Queries should be simple!
• Strive for minimal data transformation by server
– Store what you query for
– How you use data defines your schema
• Aggregation Framework can complement complex queries but
– are heavy
– required resources
33. 35
Cardinality Matters
• Product and user = many-to-many relationship
• User won't have 1000s of products in a cart
• Embedded array to represent that relationship
Shopping Cart Products
N N
34. 36
Linking vs. Embedding
• Embedding
–Great for read performance
–Heavier writes
–Great for many-to-one when many is known!
• Linking
–Allows more Flexibility
–Extra work on read workloads
–Simpler writes
39. 41
Structure your REST API
var bodyParser = require('body-parser');
var express = require('express');
var app = express();
//body parser for json payloads
app.use(bodyParser.json());
//api version
app.use('/api/v1', require('./api');
//start listening
app.listen(3000);
console.log("super REST app running");
Define a separate module for
your version implementation
40. Structure your REST API
var express = require('express');
var status = require('http-status');
var mongoose = require('mongoose');
var productSchema = require('./schemas/product');
mongoose.connect('mongodb://localhost/test');
var Product = mongoose.model('Product', productSchema);
var api = express.Router();
api.get('/products', function(req, res){
var limit = 10;
var items = Product.find().limit(10).
populate('category').
exec( function(err, docs){
if(err){
console.log('something went wrong: '+err.toString());
return res.status(status.INTERNAL_SERVER_ERROR).
json({error: err.toString()});
}
res.json({products: docs});
});
});
module.exports = api;
Express Router object
Define route and method
41. 43
GET /category/parent/:id
function errorHandler(res, err){
console.log('something went wrong: '+err.toString());
return res.status(status.INTERNAL_SERVER_ERROR).
json({error: err.toString()});
}
api.get('/products', function(req, res){ ... });
api.get('/category/parent/:id'), function(req, res){
var query = { parent: req.params.id};
var sort = {_id: -1};
Category.find(query).sort(sort).exec(function(err, docs){
if (err){
return errorHandler(res, err);
}
res.json({categories: docs});
});
});
Define route
Handle params and create
queries
42. 44
Adding Products to User's Cart
api.put('/me/cart', function(req, res){
try{
var cart = req.body.cart;
}catch(e){
return errorHandler(res, error);
}
req.user.data.cart = cart;
req.user.save(function(err, user){
if(err){
return errorHandler(res, err);
}
return res.json({user: user});
});
});
Overwrite user's cart
Let mongoose handle
validation and casting of data
Mongoose lets you be lazy
Access control using subdocuments
var userSchema = {
profile: {...},
data:{
oauth: {type:String, required: true},
cart:[{
product: {
type: mongoose.Schema.Types.ObjectId
},
quantity:{type: Number, defalt: 1,min: 1
}
}]
}
};
43. 45
Takeaways
• Express REST API on top of Mongoose
– Access control
–Business logic
– Decoupling database logic and access
46. Front End Server
var express = require('express');
var app = express();
app.use(express.static('front'));
app.get('/', function(req, res){
//load html page and let angular do all the wiring
res.sendfile('./front/index.html');
});
var server = app.listen(8080, function () {
var host = server.address().address;
var port = server.address().port;
console.log('Front end listening at http://%s:%s', host, port);
});
Using expressjs
Self "join" reference
53. 55
MEAN Stack
• Flexible Stack
• Features, features, features
–Lots of libraries available
• Strong community support
ps – runs on the best database in the world!!!
54. 56
Edx MEAN Stack Online Course
https://www.edx.org/course/introduction-mongodb-using-mean-stack-mongodbx-m101x
Quick introduction to MongoDB
MongoMK, basic components and functionality – data formats structure
Sizing considerations
Deploy strategies and architecture
Operational best practices and hints on effective monitoring, backups and automation
MEAN stack is mostly for web and mobile applications and mongodb is also a great tool to use has database backend for those kinds of applications.
CHANGE THIS WORDING
Schema validation and pseudo-joins
Ok, so you started with a model and now you decided that you actually want to migrate data together by embedding the collection together.
Well, you have options
Model view
Ok, so you started with a model and now you decided that you actually want to migrate data together by embedding the collection together.
Well, you have options