69
Build an application with MongoDB Part 1: Creating a REST API using the ME(a)N stack Chad Tindel Senior Solution Architect @ctindel

Building Your First App with MongoDB

  • Upload
    mongodb

  • View
    6.981

  • Download
    2

Embed Size (px)

Citation preview

Page 1: Building Your First App with MongoDB

Build an application with MongoDB

Part 1: Creating a REST API using the ME(a)N stack

Chad Tindel

Senior Solution Architect

@ctindel

Page 2: Building Your First App with MongoDB

2

What is all this stuff?

Page 3: Building Your First App with MongoDB

3

• Introduction to the MEAN Stack

• What is a REST API?

• Creating our REST API

• Defining our Data Model

• Real-life authentication using Stormpath

• Javascript Quirks

• WRITE YOUR TESTS FIRST

• Let’s look at some application code

Agenda

Page 4: Building Your First App with MongoDB

4

This webinar will move fast

Page 5: Building Your First App with MongoDB

5

• M = MongoDB/Mongoose.js, the most popular

nosql operational database

• E = Express.js, a lightweight web application

framework

• A = Angular.js, a robust framework for creating

HTML5 and Javascript rich web applications

• N = Node.js, a server-side javascript interpreter

The MEAN StackA modern replacement for LAMP

Page 6: Building Your First App with MongoDB

What is a REST API?

Page 7: Building Your First App with MongoDB

7

• REST = “Representation State Transfer”

• Essentially it’s just a lighter weight, though not-

standardized, alternative to SOAP and WSDL XML-

based API protocols

• Uses a client-server model, where the server is actually

an HTTP server

• Client sends HTTP verbs (GET, POST, PUT, DELETE)

along with a URL and variable parameters that are

urlencoded

• The URL tells us what object to act on

• Server replies with a result code and valid JSON

What is a REST API?

Page 8: Building Your First App with MongoDB

8

• GET – When a client wants to read an object.

• POST – Went a client wants to insert/create an

object.

• PUT – When a client wants to update an object.

• DELETE – When a client wants to delete an

object

HTTP Verbs – Mapping to CRUD

Page 9: Building Your First App with MongoDB

9

• Some common codes we might use

– 200 – “OK”

– 201 – “Created” (Used with POST)

– 400 – “Bad Request” (Perhaps missing required parameters)

– 401 – “Unauthorized” (Missing authentication parameters)

– 403 – “Forbidden” (You were authenticated but lacking required privileges)

– 404 – “Not Found”

• http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html

HTTP Result Codes

Page 10: Building Your First App with MongoDB

10

• Things you might want to say to me me at this

point:

– “REST APIs aren’t sexy”

– “How am I supposed to show this off to my boss?”

– “My clients wants to see a fancy website or mobile app”

• REST APIs are a way for you to create a data

service enabling you to easily create all your

other applications

– HTML5 / Javascript

– Android

– iOS

Why are we starting with a REST API?

Page 11: Building Your First App with MongoDB

11

• Because all the VC Money is in creating apps that

are So-Lo-Mo-Co, lots of new startups don’t even

have a web interface, such as:

– Uber

– WhatsApp

– Postmates

– Wash.io

• Creating a REST API also allows other

companies/applications to easily plug-in to your

application as well, turning your application into a

platform and making it more powerful

New companies don’t have an HTML Interface

Page 12: Building Your First App with MongoDB

Creating our REST API

Page 13: Building Your First App with MongoDB

13

• We’ll be building an RSS Aggregation application, similar

to our dearly departed Google Reader

• Our application today will have two components

– The REST API

– The Feed Grabber

• Since the point of today’s talk is to learn about creating a

REST API and not the intricacies of RSS feeds, we won’t

be discussing the feed grabber

Let’s build an application!

Page 14: Building Your First App with MongoDB

14

• We’ll need to define data models for and store the

following data:

– Users

– RSS Feeds

– Feed Entries

– User Feed Subscriptions

– Which feed entries a user has already read

• We’ll need to allow users to:

– Create an account

– Subscribe/unsubscribe to feeds

– Read feed entries

– Mark feeds/entries as read or unread

Let’s build an application!Data Models

Page 15: Building Your First App with MongoDB

15

{

"_id" : ObjectId("523b1153a2aa6a3233a913f8"),

"requiresAuthentication" : false,

"modifiedDate" : ISODate("2014-08-29T17:40:22Z"),

"permanentlyRemoved" : false,

"feedURL" : "http://feeds.feedburner.com/eater/nyc",

"title" : "Eater NY",

"bozoBitSet" : false,

"enabled" : true,

"etag" : "4bL78iLSZud2iXd/vd10mYC32BE",

"link" : "http://ny.eater.com/",

"permanentRedirectURL" : null,

"description" : "The New York City Restaurant, Bar, and Nightlife

Blog”

}

Data Model DesignFeed Collection

Page 16: Building Your First App with MongoDB

16

{

"_id" : ObjectId("523b1153a2aa6a3233a91412"),

"description" : "Buzzfeed asked a bunch of people…”,

"title" : "Cronut Mania: Buzzfeed asked a bunch of people...",

"summary" : "Buzzfeed asked a bunch of people that were…”,

"content" : [

{

"base" : "http://ny.eater.com/",

"type" : "text/html",

"value" : ”LOTS OF HTML HERE",

"language" : "en"

}

],

"entryID" : "tag:ny.eater.com,2013://4.560508",

"publishedDate" : ISODate("2013-09-17T20:45:20Z"),

"link" : "http://ny.eater.com/archives/2013/09/cronut_mania_41.php",

"feedID" : ObjectId("523b1153a2aa6a3233a913f8")

}

Data Model DesignFeed Entry Collection

Page 17: Building Your First App with MongoDB

17

{

"_id" : ObjectId("54ad6c3ae764de42070b27b1"),

"active" : true,

"email" : "[email protected]",

"firstName" : "Test",

"lastName" : "User1",

"sp_api_key_id" : "6YQB0A8VXM0X8RVDPPLRHBI7J",

"sp_api_key_secret" : "veBw/YFx56Dl0bbiVEpvbjF”,

"lastLogin" : ISODate("2015-01-07T17:26:18.996Z"),

"created" : ISODate("2015-01-07T17:26:18.995Z"),

"subs" : [ ObjectId("523b1153a2aa6a3233a913f8"),

ObjectId("54b563c3a50a190b50f4d63b")],

}

Data Model DesignUser Collection

Page 18: Building Your First App with MongoDB

18

{

"_id" : ObjectId("523b2fcc054b1b8c579bdb82"),

"read" : true,

"user_id" : ObjectId("54ad6c3ae764de42070b27b1"),

"feed_entry_id" : ObjectId("523b1153a2aa6a3233a91412"),

"feed_id" : ObjectId("523b1153a2aa6a3233a913f8")

}

Data Model DesignUser-Feed-Entry Mapping Collection

Page 19: Building Your First App with MongoDB

19

• We’ll need to allow users to:

– Create an account

– Subscribe/unsubscribe to feeds

– Read feed entries

– Mark feeds/entries as read or unread

Let’s build an application!User Actions

Page 20: Building Your First App with MongoDB

20

Creating our REST API

Route Verb Description Variables

/user/enroll POST Register a

new user

firstName

lastName

email

password

/user/resetPassword PUT Password

Reset

email

/feeds GET Get feed

subscriptions

for each user

with

description

and unread

count

/feeds/subscribe PUT Subscribe to

a new feed

feedURL

Page 21: Building Your First App with MongoDB

21

Creating our REST API

Route Verb Description Variables

/feeds/entries GET Get all entries

for feeds the

user is

subscribed to

/feeds/<feedid>/entries GET Get all entries

for a specific

feed

/feeds/<feedid> PUT Mark all

entries for a

specific feed

as read or

unread

read = <true

| false>

Page 22: Building Your First App with MongoDB

22

Creating our REST API

Route Verb Description Variables

/feeds/<feedid>/entries/<entryid> PUT Mark a

specific entry

as either read

or unread

read = <true

| false>

/feeds/<feedid> DELETE Unsubscribe

from this

particular

feed

Page 23: Building Your First App with MongoDB

Real Life Authentication with

Stormpath

Page 24: Building Your First App with MongoDB

24

Using Stormpath for Authentication

• “User Management as a

Service”

– Authentication

– Authorization

– API Keys

• REST JSON API +

– Node SDK

– Express Plugin

– Passport Plugin

Page 25: Building Your First App with MongoDB

25

Using Stormpath for Authentication

• Stormpath will give us a secret key for each “Application” we

define with them. These applications can be “Reader Prod”,

“Reader Test”, etc.

• Stormpath will give us an API Key Properties file as well

• We can define password strength requirements for each

application, like

– Must have >= 8 characters

– Must include lowercase and uppercase

– Must include a number

– Must include a non-alphabetic character

• Stormpath keeps track of all of our users and assigns them

API Keys which we can use for our REST API Authentication

Page 26: Building Your First App with MongoDB

Javascript / Node.js Overview

Page 27: Building Your First App with MongoDB

27

Creating a Node.js Application

• Install node.js

– http://nodejs.org/download/

• Node.js applications are built using a lot of library modules

• You define a package.json file describing your application and

all of it’s library dependencies

• You use the Node.js Package Manager to install a copy of

those libraries in a subdirectory of your application

(node_modules/) instead of a system directly (like /usr/lib) to

avoid the problem of different apps needing different and

conflicting library versions

• Run “npm install” and it will create the node_modules/ with all

of your required libraries

Page 28: Building Your First App with MongoDB

28

Our package.json

{

"name": "reader-api",

"main": "server.js",

"dependencies": {

"express" : "~4.10.0",

"stormpath" : "~0.7.5", "express-stormpath" : "~0.5.9",

"mongodb" : "~1.4.26”, "mongoose" : "~3.8.0",

"body-parser" : "~1.10.0”, "method-override" : "~2.3.0",

"morgan" : "~1.5.0”, "winston" : "~0.8.3”, "express-winston" : "~0.2.9",

"validator" : "~3.27.0",

"path" : "~0.4.9",

"errorhandler" : "~1.3.0",

"frisby" : "~0.8.3",

"jasmine-node" : "~1.14.5",

"async" : "~0.9.0"

}

}

Page 29: Building Your First App with MongoDB

29

Async Code in Javascript

function foo() {

someAsyncFunction(params, function(err, results) {

console.log(“one”);

});

console.log(“two”);

}

At first glance you might expect the output to be:

one

two

But actually it’s the reverse because the line that prints “one”

happens later, asynchronously, in the callback

Page 30: Building Your First App with MongoDB

30

Async Libraryhttps://github.com/caolan/async

actionArray = [

function one(cb) {

someAsyncFunction(params, function(err, results) {

if (err) {

cb(new Error(“There was an error”));

}

console.log(“one”);

cb(null);

});

},

function two(cb) {

console.log(“two”);

cb(null);

}]

]

Async.series(actionArray);

Page 31: Building Your First App with MongoDB

Write your tests first

Page 32: Building Your First App with MongoDB

32

Defining some utility librariestest/config/test_config.js

module.exports = {

url : 'http://localhost:8000/api/v1.0'

}

Page 33: Building Your First App with MongoDB

33

Defining some utility librariestest/setup_tests.js

function connectDB(callback) {

mongoClient.connect(dbConfig.testDBURL, function(err, db) {

assert.equal(null, err);

reader_test_db = db;

console.log("Connected correctly to server");

callback(0);

});

}

Page 34: Building Your First App with MongoDB

34

Defining some utility librariestest/setup_tests.js (continued)

function dropUserCollection(callback) {

console.log("dropUserCollection");

user = reader_test_db.collection('user');

if (undefined != user) {

user.drop(function(err, reply) {

console.log('user collection dropped');

callback(0);

});

} else {

callback(0);

}

},

Page 35: Building Your First App with MongoDB

35

Defining some utility librariestest/setup_tests.js (continued)

function dropUserFeedEntryCollection(callback) {

console.log("dropUserFeedEntryCollection");

user_feed_entry = reader_test_db.collection('user_feed_entry');

if (undefined != user_feed_entry) {

user_feed_entry.drop(function(err, reply) {

console.log('user_feed_entry collection dropped');

callback(0);

});

} else {

callback(0);

}

}

Page 36: Building Your First App with MongoDB

36

Defining some utility librariestest/setup_tests.js (continued)

function getApplication(callback) {

console.log("getApplication");

client.getApplications({name: SP_APP_NAME}, function(err, applications) {

console.log(applications);

if (err) {

log("Error in getApplications");

throw err;

}

app = applications.items[0];

callback(0);

});

},

Page 37: Building Your First App with MongoDB

37

Defining some utility librariestest/setup_tests.js (continued)

function deleteTestAccounts(callback) {

app.getAccounts({email: TU_EMAIL_REGEX}, function(err, accounts) {

if (err) throw err;

accounts.items.forEach(function deleteAccount(account) {

account.delete(function deleteError(err) {

if (err) throw err;

});

});

callback(0);

});

}

function closeDB(callback) {

reader_test_db.close();

}

async.series([connectDB, dropUserCollection, dropUserFeedEntryCollection,

dropUserFeedEntryCollection, getApplication, deleteTestAccounts,

closeDB);

Page 38: Building Your First App with MongoDB

38

Using frisby.js to define test casestest/create_accounts_error_spec.js

TU1_FN = "Test";

TU1_LN = "User1";

TU1_EMAIL = "[email protected]";

TU1_PW = "testUser123";

TU_EMAIL_REGEX = 'testuser*';

SP_APP_NAME = 'Reader Test';

var frisby = require('frisby');

var tc = require('./config/test_config');

frisby.create('POST missing firstName')

.post(tc.url + '/user/enroll',

{ 'lastName' : TU1_LN,

'email' : TU1_EMAIL,

'password' : TU1_PW })

.expectStatus(400)

.expectHeader('Content-Type', 'application/json; charset=utf-8')

.expectJSON({'error' : 'Undefined First Name'})

.toss()

Page 39: Building Your First App with MongoDB

39

Using frisby.js to define test casestest/create_accounts_error_spec.js (continued)

frisby.create('POST password missing lowercase')

.post(tc.url + '/user/enroll',

{ 'firstName' : TU1_FN,

'lastName' : TU1_LN,

'email' : TU1_EMAIL,

'password' : 'TESTUSER123' })

.expectStatus(400)

.expectHeader('Content-Type', 'application/json; charset=utf-8')

.expectJSONTypes({'error' : String})

.toss()

Page 40: Building Your First App with MongoDB

40

Using frisby.js to define test casestest/create_accounts_error_spec.js (continued)

frisby.create('POST invalid email address')

.post(tc.url + '/user/enroll',

{ 'firstName' : TU1_FN,

'lastName' : TU1_LN,

'email' : "invalid.email",

'password' : 'testUser' })

.expectStatus(400)

.expectHeader('Content-Type', 'application/json; charset=utf-8')

.expectJSONTypes({'error' : String})

.toss()

Page 41: Building Your First App with MongoDB

41

Using frisby.js to define test casestest/create_accounts_spec.js

TEST_USERS = [{'fn' : 'Test', 'ln' : 'User1',

'email' : '[email protected]', 'pwd' : 'testUser123'},

{'fn' : 'Test', 'ln' : 'User2',

'email' : '[email protected]', 'pwd' : 'testUser123'},

{'fn' : 'Test', 'ln' : 'User3',

'email' : '[email protected]', 'pwd' : 'testUser123'}]

SP_APP_NAME = 'Reader Test';

var frisby = require('frisby');

var tc = require('./config/test_config');

Page 42: Building Your First App with MongoDB

42

Using frisby.js to define test casestest/create_accounts_spec.js (continued)

TEST_USERS.forEach(function createUser(user, index, array) {

frisby.create('POST enroll user ' + user.email)

.post(tc.url + '/user/enroll',

{ 'firstName' : user.fn,

'lastName' : user.ln,

'email' : user.email,

'password' : user.pwd })

.expectStatus(201)

.expectHeader('Content-Type', 'application/json; charset=utf-8')

.expectJSON({ 'firstName' : user.fn,

'lastName' : user.ln,

'email' : user.email })

.toss()

});

Page 43: Building Your First App with MongoDB

43

Using frisby.js to define test casestest/create_accounts_spec.js (continued)

frisby.create('POST enroll duplicate user ')

.post(tc.url + '/user/enroll',

{ 'firstName' : TEST_USERS[0].fn,

'lastName' : TEST_USERS[0].ln,

'email' : TEST_USERS[0].email,

'password' : TEST_USERS[0].pwd })

.expectStatus(400)

.expectHeader('Content-Type', 'application/json; charset=utf-8')

.expectJSON({'error' : 'Account with that email already exists. Please choose

another email.'})

.toss()

Page 44: Building Your First App with MongoDB

44

Using frisby.js to define test casesNeed to create /tmp/readerTestCreds.js

We want to dynamically create a file that looks like this for us to use in defining

test cases that require us to authenticate a user:

TEST_USERS =

[{ "_id":"54ad6c3ae764de42070b27b1",

"email":"[email protected]",

"firstName":"Test",

"lastName":"User1",

"sp_api_key_id":”<API KEY ID>",

"sp_api_key_secret":”<API KEY SECRET>”

},

{ "_id":"54ad6c3be764de42070b27b2”,

"email":"[email protected]",

"firstName":"Test",

"lastName":"User2”,

"sp_api_key_id":”<API KEY ID>",

"sp_api_key_secret":”<API KEY SECRET>”

}];

module.exports = TEST_USERS;

Page 45: Building Your First App with MongoDB

45

Using frisby.js to define test casestests/writeCreds.js

TU_EMAIL_REGEX = new RegExp('^testuser*');

SP_APP_NAME = 'Reader Test';

TEST_CREDS_TMP_FILE = '/tmp/readerTestCreds.js';

var async = require('async');

var dbConfig = require('./config/db.js');

var mongodb = require('mongodb');

assert = require('assert');

var mongoClient = mongodb.MongoClient

var reader_test_db = null;

var users_array = null;

Page 46: Building Your First App with MongoDB

46

Using frisby.js to define test casestests/writeCreds.js (continued)

function connectDB(callback) {

mongoClient.connect(dbConfig.testDBURL, function(err, db) {

assert.equal(null, err);

reader_test_db = db;

callback(null);

});

}

function lookupUserKeys(callback) {

console.log("lookupUserKeys");

user_coll = reader_test_db.collection('user');

user_coll.find({email : TU_EMAIL_REGEX}).toArray(function(err, users) {

users_array = users;

callback(null);

});

}

Page 47: Building Your First App with MongoDB

47

Using frisby.js to define test casestests/writeCreds.js (continued)

function writeCreds(callback) {

var fs = require('fs');

fs.writeFileSync(TEST_CREDS_TMP_FILE, 'TEST_USERS = ');

fs.appendFileSync(TEST_CREDS_TMP_FILE, JSON.stringify(users_array));

fs.appendFileSync(TEST_CREDS_TMP_FILE, '; module.exports = TEST_USERS;');

callback(0);

}

function closeDB(callback) {

reader_test_db.close();

}

async.series([connectDB, lookupUserKeys, writeCreds, closeDB]);

Page 48: Building Your First App with MongoDB

48

Using frisby.js to define test casestests/feed_spec.js

TEST_USERS = require('/tmp/readerTestCreds.js');

var frisby = require('frisby');

var tc = require('./config/test_config');

var async = require('async');

var dbConfig = require('./config/db.js');

var dilbertFeedURL = 'http://feeds.feedburner.com/DilbertDailyStrip';

var nycEaterFeedURL = 'http://feeds.feedburner.com/eater/nyc';

function addEmptyFeedListTest(callback) {

var user = TEST_USERS[0];

frisby.create('GET empty feed list for user ' + user.email)

.get(tc.url + '/feeds')

.auth(user.sp_api_key_id, user.sp_api_key_secret).expectStatus(200)

.expectHeader('Content-Type', 'application/json; charset=utf-8')

.expectJSON({feeds : []})

.toss()

callback(null);

}

Page 49: Building Your First App with MongoDB

49

Using frisby.js to define test casestests/feed_spec.js

function subOneFeed(callback) {

var user = TEST_USERS[0];

frisby.create('PUT Add feed sub for user ' + user.email)

.put(tc.url + '/feeds/subscribe',

{'feedURL' : dilbertFeedURL})

.auth(user.sp_api_key_id, user.sp_api_key_secret)

.expectStatus(201)

.expectHeader('Content-Type', 'application/json; charset=utf-8')

.expectJSONLength('user.subs', 1)

.toss()

callback(null);

}

Page 50: Building Your First App with MongoDB

50

Using frisby.js to define test casestests/feed_spec.js

function subDuplicateFeed(callback) {

var user = TEST_USERS[0];

frisby.create('PUT Add duplicate feed sub for user ' + user.email)

.put(tc.url + '/feeds/subscribe',

{'feedURL' : dilbertFeedURL})

.auth(user.sp_api_key_id, user.sp_api_key_secret)

.expectStatus(201)

.expectHeader('Content-Type', 'application/json; charset=utf-8')

.expectJSONLength('user.subs', 1)

.toss()

callback(null);

}

Page 51: Building Your First App with MongoDB

51

Using frisby.js to define test casestests/feed_spec.js

function subSecondFeed(callback) {

var user = TEST_USERS[0];

frisby.create('PUT Add second feed sub for user ' + user.email)

.put(tc.url + '/feeds/subscribe',

{'feedURL' : nycEaterFeedURL})

.auth(user.sp_api_key_id, user.sp_api_key_secret)

.expectStatus(201)

.expectHeader('Content-Type', 'application/json; charset=utf-8')

.expectJSONLength('user.subs', 2)

.toss()

callback(null);

}

Page 52: Building Your First App with MongoDB

52

Using frisby.js to define test casestests/feed_spec.js

function subOneFeedSecondUser(callback) {

var user = TEST_USERS[1];

frisby.create('PUT Add one feed sub for second user ' + user.email)

.put(tc.url + '/feeds/subscribe',

{'feedURL' : nycEaterFeedURL})

.auth(user.sp_api_key_id, user.sp_api_key_secret)

.expectStatus(201)

.expectHeader('Content-Type', 'application/json; charset=utf-8')

.expectJSONLength('user.subs', 1)

.toss()

callback(null);

}

async.series([addEmptyFeedListTest, subOneFeed, subDuplicateFeed,

subSecondFeed, subOneFeedSecondUser]);

Page 53: Building Your First App with MongoDB

Can we FINALLY build our REST

API code?

Page 54: Building Your First App with MongoDB

54

Defining some utility librariesconfig/db.js

module.exports = {

url : 'mongodb://localhost/reader_test'

}

// If we wanted to have different database URLs for Dev/QA/Prod we could have

// those here

Page 55: Building Your First App with MongoDB

55

Defining some utility librariesconfig/security.js

module.exports = {

stormpath_secret_key : ‘YOUR STORMPATH APPLICATION KEY’;

}

// If we wanted to turn on database authentication we could put that here

// This file will NOT get checked into source code control for obvious reasons

Page 56: Building Your First App with MongoDB

56

Defining some utility librariesconfig/stormpath_apikey.properties

apiKey.id = YOUR STORMPATH API KEY ID

apiKey.secret = YOUR STORMPATH API KEY SECRET

Page 57: Building Your First App with MongoDB

57

Express.js Overview

• In express.js you create an “application” (app)

• That application listens on a particular port for HTTP requests

to come in

• When requests come in, they pass through a middleware

chain

– Each link in the middleware chain is given a req (the

request) object and a res object (to store the results)

– Each link can choose to do work, or pass it to the next link

• We add new middleware via app.use()

• The main middleware is called our “router”, which looks at the

URL and routes each different URL/Verb combo to a specific

handler function

Page 58: Building Your First App with MongoDB

58

Creating our application!server.js

var express = require('express');

var bodyParser = require('body-parser');

var mongoose = require('mongoose');

var stormpath = require('express-stormpath');

var routes = require("./app/routes");

var db = require('./config/db');

var security = require('./config/security');

var app = express();

var morgan = require('morgan’);

app.use(morgan);

app.use(stormpath.init(app, {

apiKeyFile: './config/stormpath_apikey.properties',

application: ‘YOUR SP APPLICATION URL',

secretKey: security.stormpath_secret_key

}));

var port = 8000;

mongoose.connect(db.url);

Page 59: Building Your First App with MongoDB

59

Creating our application!server.js (continued)

app.use(bodyParser.urlencoded({ extended: true }));

routes.addAPIRouter(app, mongoose, stormpath);

// Define our own middleware at the end of the chain to handle bad URLs

app.use(function(req, res, next){

res.status(404);

res.json({ error: 'Invalid URL' });

});

app.listen(port);

// shoutout to the user

console.log('Magic happens on port ' + port);

// expose app

exports = module.exports = app;

Page 60: Building Your First App with MongoDB

60

Defining our Mongoose Data Modelsapp/routes.js

var userSchema = new mongoose.Schema({

active: Boolean,

email: { type: String, trim: true, lowercase: true },

firstName: { type: String, trim: true },

lastName: { type: String, trim: true },

sp_api_key_id: { type: String, trim: true },

sp_api_key_secret: { type: String, trim: true },

subs: { type: [mongoose.Schema.Types.ObjectId], default: [] },

created: { type: Date, default: Date.now },

lastLogin: { type: Date, default: Date.now },

},

{ collection: 'user' }

);

userSchema.index({email : 1}, {unique:true});

userSchema.index({sp_api_key_id : 1}, {unique:true});

var UserModel = mongoose.model( 'User', userSchema );

Page 61: Building Your First App with MongoDB

61

Defining our Mongoose Data Modelsapp/routes.js (continued)

var feedSchema = new mongoose.Schema({

feedURL: { type: String, trim:true },

link: { type: String, trim:true },

description: { type: String, trim:true },

state: { type: String, trim:true, lowercase:true, default: 'new' },

createdDate: { type: Date, default: Date.now },

modifiedDate: { type: Date, default: Date.now },

},

{ collection: 'feed' }

);

feedSchema.index({feedURL : 1}, {unique:true});

feedSchema.index({link : 1}, {unique:true, sparse:true});

var FeedModel = mongoose.model( 'Feed', feedSchema );

Page 62: Building Your First App with MongoDB

62

Defining our Mongoose Data Modelsapp/routes.js (continued)

var feedEntrySchema = new mongoose.Schema({

description: { type: String, trim:true },

title: { type: String, trim:true },

summary: { type: String, trim:true },

entryID: { type: String, trim:true },

publishedDate: { type: Date },

link: { type: String, trim:true },

feedID: { type: mongoose.Schema.Types.ObjectId },

state: { type: String, trim:true, lowercase:true, default: 'new' },

created: { type: Date, default: Date.now },

},

{ collection: 'feedEntry' }

);

feedEntrySchema.index({entryID : 1});

feedEntrySchema.index({feedID : 1});

var FeedEntryModel = mongoose.model( 'FeedEntry', feedEntrySchema );

Page 63: Building Your First App with MongoDB

63

Defining our Mongoose Data Modelsapp/routes.js (continued)

var userFeedEntrySchema = new mongoose.Schema({

userID: { type: mongoose.Schema.Types.ObjectId },

feedEntryID: { type: mongoose.Schema.Types.ObjectId },

feedID: { type: mongoose.Schema.Types.ObjectId },

read : { type: Boolean, default: false },

},

{ collection: 'userFeedEntry' }

);

userFeedEntrySchema.index({userID : 1, feedID : 1, feedEntryID : 1, read : 1});

var UserFeedEntryModel = mongoose.model('UserFeedEntry',

userFeedEntrySchema );

Page 64: Building Your First App with MongoDB

64

Defining our Routesapp/routes.js (continued)

exports.addAPIRouter = function(app, mongoose, stormpath) {

app.get('/*', function(req, res, next) {

res.contentType('application/json');

next();

});

app.post('/*', function(req, res, next) {

res.contentType('application/json');

next();

});

app.put('/*', function(req, res, next) {

res.contentType('application/json');

next();

});

app.delete('/*', function(req, res, next) {

res.contentType('application/json');

next();

});

Page 65: Building Your First App with MongoDB

65

Defining our Routesapp/routes.js (continued)

var router = express.Router();

router.post('/user/enroll', function(req, res) {

logger.debug('Router for /user/enroll');

}

router.get('/feeds', stormpath.apiAuthenticationRequired, function(req, res) {

logger.debug('Router for /feeds');

}

router.put('/feeds/subscribe',

stormpath.apiAuthenticationRequired, function(req, res) {

logger.debug('Router for /feeds');

}

app.use('/api/v1.0', router);

}

Page 66: Building Your First App with MongoDB

66

Looking at our router code

Let’s go look at some real router code at:

https://github.com/ctindel/reader/blob/master/api/v1.0/app/routes.js

Page 67: Building Your First App with MongoDB

67

Starting the server and running tests

• Make sure your mongodb instance is running

– mongod

• Install the Node Libraries

– npm install

• Start the REST API server

– node server.js

• Run test cases

– node setup_tests.js

– jasmine-node create_accounts_error_spec.js

– jasmine-node create_accounts_spec.js

– node write_creds.js

– jasmine-node feed_spec.js

Page 68: Building Your First App with MongoDB

68

For More Information

Resource Location

My github repo github.com/ctindel/reader

MongoDB Downloads mongodb.com/download

Free Online Training education.mongodb.com

Webinars and Events mongodb.com/events

White Papers mongodb.com/white-papers

Case Studies mongodb.com/customers

Presentations mongodb.com/presentations

Documentation docs.mongodb.org

Additional Info [email protected]

Resource Location

Page 69: Building Your First App with MongoDB