110 |
111 | `
112 | })
113 | export class AppComponent {}
114 | ```
115 |
116 | 1. Import Forms and Http modules into the app
117 |
118 | ```javascript
119 | import { FormsModule } from '@angular/forms';
120 | import { HttpClientModule } from '@angular/common/http';
121 |
122 | // ...
123 |
124 | imports: [BrowserModule, FormsModule, HttpClientModule],
125 | ```
126 |
127 | 1. Create a model for our heroes, `hero.ts`
128 |
129 | ```bash
130 | ng g cl hero
131 | ```
132 |
133 | 1. Add a service to handle http interactions between Angular and Node
134 |
135 | ```bash
136 | ng g s hero -m app.module
137 | ```
138 |
139 | 1. Copy the code into `hero.service.ts`
140 | 1. Build the app
141 |
142 | ```bash
143 | ng build
144 | ```
145 |
146 | 1. Press the green arrow to run it!
147 | 1. Open the browser to http://localhost:3000
148 |
149 | ## Create a Cosmos DB account
150 |
151 | ### Cosmos DB setup
152 |
153 | 1. Install the Azure CLI
154 |
155 | 1. Login to Azure via the CLI
156 |
157 | ```bash
158 | # interactive login to Azure
159 | az login
160 | ```
161 |
162 | 1. Create a logical place for our Azure resources. This is a called a resource group.
163 |
164 | ```bash
165 | # Create a resource group (logical container for our Azure resources)
166 | az group create -n my-heroes-group -l "East US"
167 | ```
168 |
169 | 1. Create the Cosmos DB account and make sure it is of type MongoDB
170 |
171 | ```bash
172 | # Create our Cosmos DB
173 | az cosmosdb create -n my-heroes-db -g my-heroes-group --kind MongoDB
174 | ```
175 |
176 | ### Connecting to Mongo
177 |
178 | 1. Install the mongoose node package from npm
179 |
180 | ```bash
181 | npm install mongoose --save
182 | ```
183 |
184 | 1. Create `src/server/mongo.js` to handle mongo connections
185 |
186 | ```javascript
187 | const mongoose = require('mongoose');
188 | /**
189 | * Set to Node.js native promises
190 | * Per http://mongoosejs.com/docs/promises.html
191 | */
192 | mongoose.Promise = global.Promise;
193 |
194 | const env = require('./env/environment');
195 |
196 | // eslint-disable-next-line max-len
197 | const mongoUri = `mongodb://${env.dbName}:${env.key}@${env.dbName}.documents.azure.com:${env.cosmosPort}/?ssl=true`;
198 |
199 | function connect() {
200 | return mongoose.connect(mongoUri, { useMongoClient: true });
201 | }
202 |
203 | module.exports = {
204 | connect,
205 | mongoose
206 | };
207 |
208 | ```
209 |
210 | 1. Create the connection environment file `src/server/env/env.js`
211 |
212 | ```javascript
213 | const cosmosPort = 1234; // replace with your port
214 | const dbName = 'your-cosmos-db-name-goes-here';
215 | const key = 'your-key-goes-here';
216 |
217 | module.exports = {
218 | dbName,
219 | key,
220 | cosmosPort
221 | };
222 | ```
223 |
224 | 1. Enter the following Azure CLI commands to get the password and connection string. Copy these to your text editor
225 |
226 | ```bash
227 | # Get the key for the Cosmos DB
228 | az cosmosdb list-keys -n my-heroes-db -g my-heroes-group --query "primaryMasterKey"
229 |
230 | # Get the connection string
231 | az cosmosdb list-connection-strings -n my-cosmos-heroes -g my-heroes-db-group --query "connectionStrings[0].connectionString"
232 | ```
233 |
234 | 1. Replace your port, database name, and password/key for Cosmos DB. You can find these in the Azure portal for your Cosmos DB account.
235 |
236 | 1. Create the Hero model `src/server/hero.model.js`
237 |
238 | 1. Enter the following code to create the Hero model using a mongoose schema
239 |
240 | ```javascript
241 | const mongoose = require('mongoose');
242 | const Schema = mongoose.Schema;
243 | const heroSchema = new Schema(
244 | {
245 | id: { type: Number, required: true, unique: true },
246 | name: String,
247 | saying: String
248 | },
249 | {
250 | collection: 'heroes',
251 | read: 'nearest'
252 | }
253 | );
254 | const Hero = mongoose.model('Hero', heroSchema);
255 | module.exports = Hero;
256 | ```
257 |
258 | ### Create a service to handle heroes data using Mongo via Mongoose
259 |
260 | 1. Create `src/server/hero.service.js`
261 |
262 | ```javascript
263 | const Hero = require('./hero.model');
264 |
265 | require('./mongo').connect();
266 |
267 | function getHeroes(req, res) {
268 | const docquery = Hero.find({});
269 | docquery
270 | .exec()
271 | .then(heroes => {
272 | res.status(200).json(heroes);
273 | })
274 | .catch(error => {
275 | res.status(500).send(error);
276 | return;
277 | });
278 | }
279 |
280 | module.exports = {
281 | getHeroes
282 | };
283 | ```
284 |
285 | 1. Modify `src/server/routes.js` to use the new `hero.service.js`
286 |
287 | ```javascript
288 | const express = require('express');
289 | const router = express.Router();
290 |
291 | const heroService = require('./hero.service');
292 |
293 | router.get('/heroes', (req, res) => {
294 | heroService.getHeroes(req, res);
295 | //res.send(200, [
296 | // {"id": 10,"name": "Starlord","saying": "oh yeah"}
297 | //]);
298 | });
299 |
300 | module.exports = router;
301 | ```
302 |
303 | 1. Restart the node process in the VS Code debugger.
304 |
305 | 1. Open Postman and paste perform a *GET* http request on http://localhost:3000/api/heroes. You should see the following JSON results
306 |
307 | 1. Open the app at http://localhost:3000. You should see the app no longer has any heroes, because we are hitting the new database.
308 |
309 | ## Create the post, put and delete methods in the node server
310 |
311 | ### Create the Hero Post
312 |
313 | 1. In the `src/server/hero.service`, create the post method and the helper functions.
314 |
315 | ```javascript
316 | function postHero(req, res) {
317 | const originalHero = { id: req.body.id, name: req.body.name, saying: req.body.saying };
318 | const hero = new Hero(originalHero);
319 | hero.save(error => {
320 | if (checkServerError(res, error)) return;
321 | res.status(201).json(hero);
322 | console.log('Hero created successfully!');
323 | });
324 | }
325 |
326 | function checkServerError(res, error) {
327 | if (error) {
328 | res.status(500).send(error);
329 | return error;
330 | }
331 | }
332 |
333 | module.exports = {
334 | getHeroes,
335 | postHero
336 | };
337 | ```
338 |
339 | 1. In the `src/server/routes.js`, call the post function
340 |
341 | ```javascript
342 | router.post('/hero', (req, res) => {
343 | heroService.postHero(req, res);
344 | });
345 | ```
346 |
347 | 1. Restart the node process in the VS Code debugger.
348 |
349 | 1. Open the app at http://localhost:3000. Add a hero
350 |
351 | ### Create the PUT and DELETE
352 |
353 | 1. In the `src/server/hero.service`, create the post method and the helper functions.
354 |
355 | ```javascript
356 | function putHero(req, res) {
357 | const id = parseInt(req.params.id, 10);
358 | const updatedHero = {
359 | id: id,
360 | name: req.body.name,
361 | saying: req.body.saying
362 | };
363 | Hero.findOne({ id: id }, (error, hero) => {
364 | if (checkServerError(res, error)) return;
365 | if (!checkFound(res, hero)) return;
366 |
367 | hero.name = updatedHero.name;
368 | hero.saying = updatedHero.saying;
369 | hero.save(error => {
370 | if (checkServerError(res, error)) return;
371 | res.status(200).json(hero);
372 | console.log('Hero udpated successfully!');
373 | });
374 | });
375 | }
376 |
377 | function deleteHero(req, res) {
378 | const id = parseInt(req.params.id, 10);
379 | Hero.findOneAndRemove({ id: id })
380 | .then(hero => {
381 | if (!checkFound(res, hero)) return;
382 | res.status(200).json(hero);
383 | console.log('Hero deleted successfully!');
384 | })
385 | .catch(error => {
386 | if (checkServerError(res, error)) return;
387 | });
388 | }
389 |
390 | function checkFound(res, hero) {
391 | if (!hero) {
392 | res.status(404).send('Hero not found.');
393 | return;
394 | }
395 | return hero;
396 | }
397 |
398 | module.exports = {
399 | getHeroes,
400 | postHero,
401 | putHero,
402 | deleteHero
403 | };
404 | ```
405 |
406 | 1. In the `src/server/routes.js`, call the post function
407 |
408 | ```javascript
409 | router.post('/hero', (req, res) => {
410 | heroService.postHero(req, res);
411 | });
412 | ```
413 |
414 | 1. Restart the node process in the VS Code debugger.
415 |
416 | 1. Open the app at http://localhost:3000. Get, Add, Update, Delete !
417 |
--------------------------------------------------------------------------------
/TUTORIAL_BREAKDOWN.md:
--------------------------------------------------------------------------------
1 | # MEAN app with Cosmos DB
2 |
3 | ## Create and run the API
4 |
5 | We'll create the Angular app and add the Node and Express code to handle API routing.
6 |
7 | ### Create the app
8 |
9 | 1. Install the Angular CLI
10 | 1. Generate a new Angular app
11 |
12 | ### Node and Express
13 |
14 | 1. Install Express, and body parser.
15 | 1. Create a new folder and file for `src/server/index.js`, add basic node/express logic
16 | 1. Create `src/server/routes.js` copy the routes in
17 | 1. Create a `/api/get` route (with hard coded heroes)
18 | 1. Create a `/api` route in `src/server/index.js` (link to routes.js)
19 | 1. Create a VS Code debug configuration for Node (Press debug, press configure, select Node.js, press green button)
20 | 1. Change the launch config's launch program to our node server
21 | 1. Press the green arrow to run the node server
22 | 1. Open Postman and paste perform a *GET* http request on http://localhost:3000/api/heroes. You should see the following JSON results
23 |
24 | ## Adding the A in MEAN
25 |
26 | ### The Heroes UI
27 |
28 | 1. Add a component to list Heroes. The `--flat` flag adds the component without putting it in its own sub folder.
29 | 1. Copy this code into `heroes.component.html`
30 | 1. Copy this code into `heroes.component.ts`
31 | 1. Copy the styles.scss code
32 | 1. Add the hero component's selector to the `app.component.ts`
33 | 1. Import Forms and Http modules into the app
34 | 1. Create a model for our heroes, `hero.ts`
35 | 1. Add a service to handle http interactions between Angular and Node
36 | 1. Copy the code into `hero.service.ts`
37 | 1. Build the app
38 | 1. Create a VS Code debug configuration for Node, and press the green arrow to run it!
39 | 1. Open the browser to http://localhost:3000
40 |
41 | ## Cosmos DB setup
42 |
43 | 1. Install the Azure CLI
44 | 1. Login to Azure via the CLI
45 | 1. Create a logical place for our Azure resources. This is a called a resource group.
46 | 1. Create the Cosmos DB account and make sure it is of type MongoDB
47 | 1. Check it out in the Portal
48 |
49 | ### Connecting to Mongo
50 |
51 | 1. Install the mongoose node package from npm
52 | 1. Create `src/server/mongo.js` to handle mongo connections
53 | 1. Create the connection environment file `src/server/env/environment.js`
54 | 1. Enter the following Azure CLI commands to get the password and connection string. Copy these to your text editor, as we'll need them in a moment.
55 | 1. Replace your port, database name, and password/key for Cosmos DB. You can find these in the Azure portal for your Cosmos DB account.
56 | 1. Create the Hero model `src/server/hero.model.js`
57 | 1. Enter the following code to create the Hero model using a mongoose schema
58 | 1. Create a service to handle heroes data using Mongo via Mongoose `src/server/hero.service.js`
59 | 1. Modify `src/server/routes.js` to use the new `hero.service.js`
60 | 1. Restart the node process in the VS Code debugger.
61 | 1. Open Postman and paste perform a *GET* http request on http://localhost:3000/api/heroes. You should see the following JSON results
62 | 1. Open the app at http://localhost:3000. You should see the app no longer has any heroes, because we are hitting the new database.
63 |
64 | ## Create the post, put and delete methods in the node server
65 |
66 | ### Create the Hero Post
67 |
68 | 1. In the `src/server/hero.service`, create the post method and the helper functions.
69 | 1. In the `src/server/routes.js`, call the post function
70 | 1. Restart the node process in the VS Code debugger.
71 | 1. Open the app at http://localhost:3000. Add a hero
72 |
73 | ### Create the PUT and DELETE
74 |
75 | 1. In the `src/server/hero.service`, create the post method and the helper functions.
76 | 1. In the `src/server/routes.js`, call the post function
77 | 1. Restart the node process in the VS Code debugger.
78 | 1. Open the app at http://localhost:3000. Get, Add, Update, Delete !
79 |
--------------------------------------------------------------------------------
/VIDEOS.md:
--------------------------------------------------------------------------------
1 | # Learning to Build MEAN Apps with Cosmos DB
2 |
3 | ## Overview
4 |
5 | This video series shows how to build MEAN apps using Cosmos DB. Each video in the series will be a couple of minutes and show how each part of the application works.
6 |
7 | ## Videos
8 |
9 | ### Part 1 - Introduction
10 |
11 | Cosmos DB makes it easy to step right in where I used to use Mongo, because it lets me use the same exact APIs I used with Mongo. Are you using the mongo npm module? No problem. Do you prefer mongoose? That works too. I just love that I don't have to change how I work! This video takes a look at what we'll build.
12 |
13 | [](https://www.youtube.com/watch?v=vlZRP0mDabM)
14 |
15 | ### Part 2 - Node.js and Express
16 |
17 | One of the best ways to create a MEAN app is to start with the [Angular CLI](https://github.com/angular/angular-cli) to generate a starting point. We'll use this and extend it to include Node.js and Express.js, to serve our future APIs to the Angular app. Then we'll test out an API to make sure it works.
18 |
19 | [](https://www.youtube.com/watch?v=lIwJIYcGSUg)
20 |
21 | ### Part 3 - Angular and Express APIs
22 |
23 | I create the UI with Angular and connect to the Express APIs. I use the [Angular CLI](https://github.com/angular/angular-cli) to generate a the component and service the app needs. Then I build the app and show it in the browser.
24 |
25 | [](https://www.youtube.com/watch?v=MnxHuqcJVoM)
26 |
27 | ### Part 4 - Creating Cosmos DB
28 |
29 | Using the Azure CLI, I create the Cosmos DB account to represent a MongoDB model database and deploy it to Azure. Then I show how to view what we created in the Azure portal.
30 |
31 | [](https://www.youtube.com/watch?v=hfUM-AbOh94)
32 |
33 | ### Part 5 - Querying Cosmos DB
34 |
35 | This next video shows how to connect to the MongoDB database with Azure Cosmos DB, using Mongoose, and query it for data.
36 |
37 | [](https://www.youtube.com/watch?v=sI5hw6KPPXI)
38 |
39 | ### Part 6 - POST, PUT, and DELETE
40 |
41 | This next video shows how to POST, PUT, and DELETE to the MongoDB database with Azure Cosmos DB, using Mongoose.
42 |
43 | [](https://www.youtube.com/watch?v=Y5mdAlFGZjc)
44 |
--------------------------------------------------------------------------------
/e2e/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { AngularCosmosdbPage } from './app.po';
2 |
3 | describe('angular-cosmosdb App', () => {
4 | let page: AngularCosmosdbPage;
5 |
6 | beforeEach(() => {
7 | page = new AngularCosmosdbPage();
8 | });
9 |
10 | it('should display welcome message', () => {
11 | page.navigateTo();
12 | expect(page.getParagraphText()).toEqual('Welcome to toh!!');
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/e2e/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, by, element } from 'protractor';
2 |
3 | export class AngularCosmosdbPage {
4 | navigateTo() {
5 | return browser.get('/');
6 | }
7 |
8 | getParagraphText() {
9 | return element(by.css('app-root h1')).getText();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/e2e/tsconfig.e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/e2e",
5 | "module": "commonjs",
6 | "target": "es5",
7 | "types": [
8 | "jasmine",
9 | "jasminewd2",
10 | "node"
11 | ]
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/0.13/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular/cli'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular/cli/plugins/karma')
14 | ],
15 | client:{
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | reports: [ 'html', 'lcovonly' ],
20 | fixWebpackSourcePaths: true
21 | },
22 | angularCli: {
23 | environment: 'dev'
24 | },
25 | reporters: ['progress', 'kjhtml'],
26 | port: 9876,
27 | colors: true,
28 | logLevel: config.LOG_INFO,
29 | autoWatch: true,
30 | browsers: ['Chrome'],
31 | singleRun: false
32 | });
33 | };
34 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-cosmosdb",
3 | "version": "0.0.0",
4 | "license": "MIT",
5 | "scripts": {
6 | "ng": "ng",
7 | "start": "node src/server/index.js",
8 | "start-ng": "ng serve",
9 | "build": "ng build",
10 | "test": "ng test",
11 | "lint": "ng lint",
12 | "e2e": "ng e2e"
13 | },
14 | "private": true,
15 | "dependencies": {
16 | "@angular/animations": "^4.2.4",
17 | "@angular/common": "^4.2.4",
18 | "@angular/compiler": "^4.2.4",
19 | "@angular/core": "^4.2.4",
20 | "@angular/forms": "^4.2.4",
21 | "@angular/http": "^4.2.4",
22 | "@angular/platform-browser": "^4.2.4",
23 | "@angular/platform-browser-dynamic": "^4.2.4",
24 | "@angular/router": "^4.2.4",
25 | "core-js": "^2.4.1",
26 | "rxjs": "^5.4.2",
27 | "zone.js": "^0.8.14",
28 | "express": "^4.15.3",
29 | "mongoose": "^4.11.1"
30 | },
31 | "devDependencies": {
32 | "@angular/cli": "1.3.1",
33 | "@angular/compiler-cli": "^4.2.4",
34 | "@angular/language-service": "^4.2.4",
35 | "@types/jasmine": "~2.5.53",
36 | "@types/jasminewd2": "~2.0.2",
37 | "@types/node": "~6.0.60",
38 | "codelyzer": "~3.1.1",
39 | "jasmine-core": "~2.6.2",
40 | "jasmine-spec-reporter": "~4.1.0",
41 | "karma": "~1.7.0",
42 | "karma-chrome-launcher": "~2.1.1",
43 | "karma-cli": "~1.0.1",
44 | "karma-coverage-istanbul-reporter": "^1.2.1",
45 | "karma-jasmine": "~1.1.0",
46 | "karma-jasmine-html-reporter": "^0.2.2",
47 | "protractor": "~5.1.2",
48 | "ts-node": "~3.2.0",
49 | "tslint": "~5.3.2",
50 | "typescript": "~2.3.3"
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // Protractor configuration file, see link for more information
2 | // https://github.com/angular/protractor/blob/master/lib/config.ts
3 |
4 | const { SpecReporter } = require('jasmine-spec-reporter');
5 |
6 | exports.config = {
7 | allScriptsTimeout: 11000,
8 | specs: [
9 | './e2e/**/*.e2e-spec.ts'
10 | ],
11 | capabilities: {
12 | 'browserName': 'chrome'
13 | },
14 | directConnect: true,
15 | baseUrl: 'http://localhost:4200/',
16 | framework: 'jasmine',
17 | jasmineNodeOpts: {
18 | showColors: true,
19 | defaultTimeoutInterval: 30000,
20 | print: function() {}
21 | },
22 | onPrepare() {
23 | require('ts-node').register({
24 | project: 'e2e/tsconfig.e2e.json'
25 | });
26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/src/client/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-root',
5 | template: `
6 |