├── db ├── clouddb.js ├── settings.js └── vacations.js ├── index.js ├── .gitignore ├── package.json ├── models └── vacations.js ├── README.md ├── tests └── TestDbOps.js ├── api └── v1 │ └── vacations.js └── data └── vacations.js /db/clouddb.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Setup the Database URL 3 | */ 4 | 5 | // mongodb+srv://admin:@cluster0-46e5h.mongodb.net/test?retryWrites=true&w=majority 6 | 7 | const DB_USER = "admin" 8 | const DB_PASSWORD = "admin123" 9 | const DB_NAME = "acmetravel" 10 | const CLUSTER_HOST = "cluster0-46e5h.mongodb.net" 11 | 12 | // Setup the DB URI 13 | exports.DB_URI= "mongodb+srv://"+DB_USER+":"+DB_PASSWORD+"@"+CLUSTER_HOST+"/"+DB_NAME+"?retryWrites=true&w=majority" -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Launches the API Listener 3 | * 4 | * Checkout the API implementation in api/v1/vacations.js 5 | */ 6 | 7 | // Setup the DB_URI 8 | process.env.DB_URI = require("./db/clouddb").DB_URI 9 | 10 | var express = require('express') 11 | var bodyParser = require('body-parser') 12 | 13 | var router = express.Router(); 14 | require('./api/v1/vacations')(router); 15 | 16 | // Create the express app 17 | app = express(); 18 | // Setup the body parser 19 | //app.use(bodyParser.urlencoded({extended: true})); 20 | app.use(bodyParser.json());//{type: '*/*'})); 21 | 22 | // Setup the app to use the router 23 | app.use(router); 24 | 25 | // Start the listener 26 | app.listen(3000); 27 | console.log('Listening on 3000') 28 | 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | 40 | credentials -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "uridemo", 3 | "version": "1.0.0", 4 | "description": "This is part of the course on \"REST API Development\"", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/rajrin/URIDemo.git" 12 | }, 13 | "keywords": [ 14 | "rest", 15 | "api", 16 | "architecture", 17 | "constraints" 18 | ], 19 | "author": "acloudfan.com", 20 | "license": "ISC", 21 | "bugs": { 22 | "url": "https://github.com/rajrin/URIDemo/issues" 23 | }, 24 | "homepage": "https://github.com/rajrin/URIDemo#readme", 25 | "dependencies": { 26 | "body-parser": "^1.19.0", 27 | "express": "^4.17.1", 28 | "mongoose": "^5.7.3" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /db/settings.js: -------------------------------------------------------------------------------- 1 | /** 2 | * All database settings done in this file 3 | */ 4 | 5 | var mongoose = require('mongoose') 6 | // This would make mongoose use native Promises 7 | mongoose.Promise = global.Promise; 8 | 9 | // This environment property is used for getting the URI for MongoDB 10 | // 'mongodb://:@ds163387.mlab.com:63387/acme123' 11 | var uri = process.env.DB_URI 12 | 13 | // No need to provid ethe user /password separately its part of the URI 14 | // var options = {user:process.env.DB_USER, pass:process.env.DB_PASSWORD} 15 | 16 | mongoose.connect(process.env.DB_URI, { 17 | // useMongoClient: true 18 | useNewUrlParser: true, 19 | useUnifiedTopology: true 20 | }); 21 | 22 | // Setup event listeners for the mongoose connections 23 | mongoose.connection.on('error', function(err){ 24 | console.log('Mongoose connection error') 25 | console.log(err) 26 | }) 27 | // events 28 | mongoose.connection.on('disconnected', function(){ 29 | console.log('Mongoose disconnected') 30 | }) 31 | mongoose.connection.on('open', function(){ 32 | console.log('Mongoose connected') 33 | }) 34 | 35 | // export the mongoose & db 36 | exports.mongoose = mongoose; 37 | exports.db = mongoose.connection; 38 | 39 | -------------------------------------------------------------------------------- /db/vacations.js: -------------------------------------------------------------------------------- 1 | /** 2 | * All database operations related to the vacations collection will reside in this file 3 | */ 4 | var model = require('../models/vacations') 5 | var settings = require('../db/settings') 6 | 7 | // CREATE the vacation package 8 | exports.save = function (data, callback) { 9 | 10 | new model.Vacations(data).save(function (err, inserted) { 11 | callback(err, inserted) 12 | 13 | }) 14 | } 15 | 16 | // CREATE multiple vacation packages 17 | exports.saveMany = function (rows, callback) { 18 | 19 | model.Vacations.insertMany(rows, function (err, docs) { 20 | callback(err, docs) 21 | }) 22 | 23 | } 24 | 25 | // UPDATE the vacation packages 26 | // http://mongoosejs.com/docs/api.html#model_Model.update 27 | exports.update = function (criteria, doc, callback) { 28 | // Replaced .update() with .updateMany() as .update() is deprecated 29 | model.Vacations.updateMany(criteria, doc, function (err, data) { 30 | callback(err, data) 31 | 32 | }) 33 | } 34 | 35 | // RETRIEVE vacation packages based on criteria 36 | exports.select = function (criteria, callback) { 37 | model.Vacations.find(criteria, function (err, data) { 38 | callback(err, data) 39 | }) 40 | } -------------------------------------------------------------------------------- /models/vacations.js: -------------------------------------------------------------------------------- 1 | /** 2 | * "REST API Course" 3 | * 4 | * Model for the ACME vacation package. 5 | */ 6 | 7 | var settings = require('../db/settings') 8 | 9 | 10 | var VacationsSchema = settings.mongoose.Schema( 11 | { 12 | // Name of the vacation package - BAHAMAS1000 - primary Key 13 | name: {type:String, required:[true,'name is needed']}, 14 | description : {type:String, required:true}, 15 | // ACME offers resorts & cruise vacation package 16 | type: {type:String, enum:['resort','cruise']}, 17 | // Destination city 18 | destinations : [{city:String, country:String}], 19 | // Includes - what all does the package Include 20 | includes : [{ 21 | what:{type:String, enum:['flight', 'meals','cruise','hotel','rentalcar','excursions','misc']}, 22 | description:{type:String, required:false} 23 | }], 24 | numberOfNights:{type: Number, required:true, min:1, max:31}, 25 | // Price per person 26 | pricePP: Number, 27 | // Special offers 28 | offer : { 29 | discount: Number, 30 | description : String, 31 | expires:{type:Date, required:false} 32 | }, 33 | // Till what date is the package valid 34 | validTill:{type:Date, required:true}, 35 | // Package may get sold out 36 | soldout: {type:Boolean, required:true, default:false}, 37 | // Link to pictures 38 | pictures:{type:[String]} 39 | } 40 | ); 41 | 42 | // Export the model 43 | exports.Vacations = settings.mongoose.model('vacation', VacationsSchema) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Created as part of the course on "REST API". 2 | # http://www.acloudfan.com/learn-REST-API 3 | # Demonstrates the setting up of URI for the REST API 4 | 5 | # UDEMY Discount Link 6 | https://www.udemy.com/course/rest-api/?referralCode=ADE763DAA790A3F0D211 7 | 8 | 9 | Change Log 10 | ========== 11 | October 2019 Converted the code to use MongoDB Cloud 12 | Updated the package.json with latest versions 13 | Took care of the depercations 14 | Updated the index.js for DB parameters setting 15 | 16 | December 2017 Updated the DB Connection code 17 | Data updated for the testing. 18 | Instead of providing the User/Password now the URI for DB is used in the format: mongodb://:@SERVER:PORT/DB-NAME 19 | 20 | Setup 21 | ===== 22 | 1. Clone this project on local file system 23 | > git clone https://github.com/acloudfan/REST-API-Course-V2.git 24 | 2. Pre-requisistes 25 | a. Understanding of node/npm 26 | b. An instance of MongoDB available either locally or remotely 27 | c. Import the data available in the repository 28 | 3. Run > npm install 29 | Deploys the packages 30 | express 31 | body-parser 32 | mongoose 33 | 34 | 4. Switch between branches 35 | > git checkout errorhandling 36 | 37 | 38 | Create from scratch 39 | =================== 40 | 1. create the project 41 | 2. git init 42 | 3. add .gitignore 43 | 4. npm init 44 | 5. npm install the packages (express,body-parser,mongoose) 45 | 46 | 6. create folder = models 47 | 48 | Helpful Links 49 | ============= 50 | https://github.com/felixge/node-style-guide 51 | https://blog.risingstack.com/node-js-best-practices/ -------------------------------------------------------------------------------- /tests/TestDbOps.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple tests for all DB operations 3 | * 4 | * Adds the test data to the Database = vacation Collection = vacations 5 | * Updated 1/1/2020 6 | */ 7 | 8 | 9 | // Setup the DB_URI 10 | process.env.DB_URI = require("../db/clouddb").DB_URI 11 | 12 | //Test#1 Insert the Vacation data 13 | var db = require('../db/vacations') 14 | var data = require('../data/vacations') 15 | 16 | 17 | // Save a single row 18 | db.save(data.SingleRow,function(err, saved){ 19 | if(err){ 20 | console.log("Failed single row save") 21 | //console.log(err) 22 | //process.exit(1) 23 | } else { 24 | console.log("Success - Save single row - %s",saved.name) 25 | } 26 | }); 27 | 28 | // Save multiple rows 29 | db.saveMany(data.MultipleRows,function(err, docs){ 30 | if(err){ 31 | console.log("Failed multiple row insert") 32 | //console.log(err) 33 | //process.exit(1) 34 | } else { 35 | console.log("Success - Multiple rows inserted - %d",docs.length) 36 | } 37 | }); 38 | 39 | // Select vacations with some criteria 40 | var selectCriteria = {validTill : {$gt : new Date()}} 41 | db.select(selectCriteria, function(err, data){ 42 | if(err){ 43 | console.log("Failed to get vacations : %s",criteria) 44 | console.log(err) 45 | } else { 46 | console.log("Successfully selected %d documents for %s", data.length, JSON.stringify(selectCriteria)) 47 | } 48 | }); 49 | 50 | // Update the vacations 51 | var updateCriteria = {name:'BAHAMAS1000'} 52 | var doc = {description:'UPDATED Desc for TESTING'} 53 | db.update(updateCriteria,doc,function(err, doc){ 54 | if(err){ 55 | console.log("Failed to get update") 56 | console.log(err) 57 | } else { 58 | console.log("Successfully updated with criteria %s", updateCriteria) 59 | } 60 | }) 61 | -------------------------------------------------------------------------------- /api/v1/vacations.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Contains the definition of the API endpoints for vacation packages 3 | * Updated 1/1/2020 4 | */ 5 | // As a best practice keep the resource name same as the file name 6 | var RESOURCE_NAME = 'vacations'; 7 | var VERSION = 'v1'; 8 | var URI = '/' + VERSION + '/' + RESOURCE_NAME; 9 | 10 | // Setup the vacations db 11 | var db = require('../../db/vacations') 12 | 13 | module.exports = function(router){ 14 | 'use strict'; 15 | 16 | // RETRIEVE all active vacation packages 17 | // Active = validTill >= Today's date 18 | 19 | // /v1/Vacations 20 | router.route(URI).get(function(req, res,next){ 21 | console.log("GET Vacations") 22 | //1. Setup query riteria for the active pacakages 23 | var criteria = {validTill : {$gte : new Date()}} 24 | 25 | //2. execute the query 26 | db.select(criteria, function(err,docs){ 27 | 28 | if(err){ 29 | console.log(err) 30 | res.status(500) 31 | res.send("Error connecting to db") 32 | } else { 33 | if(docs.length == 0){ 34 | res.status(404) 35 | } 36 | console.log("Retrieved vacations = %d",docs.length) 37 | res.send(docs) 38 | } 39 | }); 40 | }); 41 | 42 | // CREATE new vacation packages 43 | router.route(URI).post(function(req, res,next){ 44 | console.log("POST Vacations") 45 | 46 | //1. Get the data 47 | var doc = req.body; 48 | 49 | //2. Call the insert method 50 | db.save(doc, function(err,saved){ 51 | if(err){ 52 | // The returned error need to be defined better - in this example it is being left as is 53 | res.status(400).send(err) 54 | } else { 55 | res.send(saved) 56 | } 57 | }); 58 | }); 59 | } 60 | -------------------------------------------------------------------------------- /data/vacations.js: -------------------------------------------------------------------------------- 1 | 2 | exports.SingleRow = { 3 | "name":"BAHAMAS1000", 4 | "description":"4 Nights all paid Resort vacation in Bahamas at the Atlantis resort", 5 | "type": "resort", 6 | "destinations": [ 7 | { 8 | "city": "Nassau", 9 | "country": "Bahamas" 10 | } 11 | ], 12 | "includes": [ 13 | { 14 | "what": "hotel", 15 | "description": "King or Queen Bed room, can accomodate 2 Adults and 2 Children" 16 | }, 17 | { 18 | "what": "meals", 19 | "description": "Buffet style, available 24 hours in various resort restaurants" 20 | } 21 | ], 22 | "numberOfNights": 4, 23 | "pricePP": 300, 24 | "offer": { 25 | "discount": 0.1, 26 | "description": "End of year sale", 27 | "expires": "1/31/2025" 28 | }, 29 | "validTill": "1/31/2025", 30 | "soldout": false 31 | } 32 | 33 | exports.MultipleRows = [ 34 | { 35 | "name":"HAWAII1000", 36 | "description":"10 Nights all paid Resort vacation on Big Island", 37 | "type": "resort", 38 | "destinations": [ 39 | { 40 | "city": "Maui", 41 | "country": "Hawaii" 42 | } 43 | ], 44 | "includes": [ 45 | { 46 | "what": "hotel", 47 | "description": "King or Queen Bed room, can accomodate 2 Adults and 2 Children" 48 | }, 49 | { 50 | "what": "meals", 51 | "description": "Buffet style, available 24 hours in various resort restaurants" 52 | } 53 | ], 54 | "numberOfNights": 10, 55 | "pricePP": 800, 56 | "offer": { 57 | "discount": 0.25, 58 | "description": "End of year sale", 59 | "expires": "1/31/2025" 60 | }, 61 | "validTill": "1/31/2025", 62 | "soldout": false 63 | 64 | }, 65 | { 66 | "name":"CRUISEBAHAMAS1000", 67 | "description":"6 Nights cruise to Bahamas from Miami, FL in luxury line 'Carnival, Ocean Beauty'", 68 | "type": "cruise", 69 | "destinations": [ 70 | { 71 | "city": "Nassau", 72 | "country": "Bahamas" 73 | }, 74 | { 75 | "city": "Jamaica", 76 | "country": "Jamaica" 77 | } 78 | ], 79 | "includes": [ 80 | { 81 | "what": "cruise", 82 | "description": "Regular cruise suite can easily accomodate 2 adults and 2 children" 83 | }, 84 | { 85 | "what": "meals", 86 | "description": "Buffet style, available 24 hours in various restaurants" 87 | } 88 | ], 89 | "numberOfNights": 10, 90 | "pricePP": 800, 91 | "offer": { 92 | "discount": 0.25, 93 | "description": "End of year sale", 94 | "expires": "1/31/2025" 95 | }, 96 | "validTill": "1/31/2025", 97 | "soldout": false 98 | 99 | }] 100 | --------------------------------------------------------------------------------