- All steps here originate from this tutorial, with the exception of Heroku deployment and AWS Electric Beanstalk deployment step, which I have modified to fit my own workflow
-
master
..for deployment to Heroku -
aws-elastic-beanstalk
..for deployment to AWS Electric Beanstalk
-
Angular app is tied into Node app via
views/index.hbs
-
Open 4 terminal windows and run:
-
mongod
-
mongo
-
npm run build
-
npm start
-
Visit
localhost:3000
-
routes/messages.js:
obj: result
//line 64...this ties messages.service.ts from Angular/front end in with backend DB
-
Connect front end to back end DB:
-
add/edit/delete message (in the front message array)
-
use message service to connect backend and then post/patch/delete the data (MessageListComponent uses this.messageService.getMessages connect to router.get, which gets messages from the data in the DB and uses this.messages = messages; to update the front data and then also in MessageListComponent use*ngFor="let message of messages to let us see the updated front data from backend data in the front end.
-
-
Start with backend in
routes/user.js
-
Create JWT token
npm i --save jsonwebtoken
- Connect back end to front end:
-
Go to
auth/auth.service.ts
- create signin method
-
Go to
auth/signin.component.ts
-
create new User in onSubmit() with
http://localhost:3000/user/signin
leading tosignin
path pointing tosignin
path inroutes/user.js
-
import { User } from './user.model';
-
import { Router } from '@angular/router';
-
constructor(private authService: AuthService, private router: Router){}
-
import { AuthService } from "./auth.service";
-
npm run build
-
-
-
in auth/auth.service.ts:
- add logout() method
-
in auth/logout-component.ts:
import { AuthService } from "./auth.service";
-
add authService to constructor + import Router....
-
Call authService function from authService:
this.authService.logout()
-
-
in auth/auth.service.ts:
- add isLoggedIn() method
-
in auth/authentication.component.ts:
-
inject authService via constructor function +
import { AuthService } from "./auth.service";
-
add
isLoggedIn()
method -
add
*ngIf
directives to links in template
-
-
in routes/messages.js:
-
add
jwt.verify
viarouter.use
,which will be reached on each request ...usesecret
to validate incoming token -
secret
has to match thesecret
created when secret was created viaroutes/user.js
via token variable -
var jwt = require('jsonwebtoken');
-
- remove current messages from DB:
db.messages.remove({})
-
In
routes/messages.js
-
retrieve user from token in
routes/user.js
-
use
decode()
method inrouter.post
-
pass decoded object to user field in
routes/user.js
viaUser.findById
-
create
message.save(){}
function and push messages to array to messages onUser
-
import User model:
var User = require('../models/user');
-
-
In
models/message.js
:- In order to delete a message, need to delete from array of messages...also need to make sure that UserId is also deleted from messages array
- set up
schema.post(){}
function to listen forremove
action and passmessage.user
toUserId
..so whenever a message is deleted, the message array on the User is updated to prevent any redundant data in DB
-
Add a token for creating &/or sending a message and for updating/deleting
-
In
messages/message.service.ts
:-
via
addMessage(message: Message)
method check 1st to see if there is a token in local storage viaconst token = localStorage.getItem('token')
-
set up query string to append to end of url if token exists
-
add
token
toupdateMessage()
and append it AFTERid
, then repeat withdeleteMessage()
-
recompile
ie.,npm run build
NOTE: returns error in shell:
`RangeError: Maximum call stack size exceeded` - https://www.udemy.com/angular-2-and-nodejs-the-practical-guide/learn/v4/questions/2829784 - change `user.messages.push(result);` in `routes/messages.js` to : ` user.messages.push( mongoose.Types.ObjectId(result._id) ); ` + import mongoose - restart `mongo` ` db.messages.find() `//check to see if user ID was added to messages collection ` db.users.find() `//check to see if array of messages + messageId was added to User collection
-
- ensure that only users that create a post are able to delete/edit messages
-
routes/messages.js
-
need to fetch user in patch function + check to see if this is user that created message
-
add
token
topatch
route/method -
check to see if
message.user
is !same as the user stored in token in patch route -
implement/repeat same with delete route/method
-
restart server, test by creating new user(s) and creating/editing messages, etc....
-
-
Conditionally show buttons + show correct user:
-
routes/messages.js:
-
adjust
get
route:- add
.populate()
method ....a mongoose method which allows to expand data that is being retrieved ..by default would only retrieve the message object, with.populate()
can create reference toUser
model..this is tied in/reference inmodels/message.js
viaref: 'User'
key/value pair, which is a connection to another object in DB ..can now tell Mongoose to expand object so that user field onot only holds user id, but the complete user object with all of its fields
- add
-
-
- Fetch complete user in frontend via messages.service.ts
getMessages()
:
- get user firstName in `let message of messages`
for loop
- populate `const message` with correct data in `addMessage()` method
* Create new message + test to see if username is attached to message
-
In
message-component.ts
:- add
belongsToUser()
method
NOTE: Important validation always happens on the backend, validation on the front end is strictly for the user experience
- add
-
In
message-component.html
:-
add
*ngIf="belongsToUser()"
..ie., show buttons based on user state -
recompile + reload
-
- if someone steals your Token, they may access your API. That's why tokens should be short-living and not be stored for long time durations. It's of course also a good idea to send HTTPS requests only. Your token can then only be stolen if the attacker somehow has access to your computer.
- IP whitelisting also add a layer of security
* create new `errors directory` in `app`
- create `error.service.ts`, ` error.model.ts`(to define how error should look on front end), `error.component.ts`, & `error.component.html`
* `error.component.html`:
- set up Bootstrap modal (HTML only)
- create `.backdrop` class to show/hide conditionally
* `error.component.ts`:
- create property `error` of type `Error` + `display = none`
- create `onErrorHandled()` method
* `error.component.html`
- add ngStyle directive w/ property binding to `.backdrop` to display...also add to `.modal`
- Use string interpolation to output error title and message:
- `{{ error?.title }}` & `{{ error?.message }}`//'?' helps to avoid errors
- configure close button:
- ` (click)="onErrorHandled()" `
* `app.component.html`:
- add/append ` <app-error></app-error> `
* `app.module.ts`:
- ` import { ErrorComponent } ....
import { ErrorService } ....`
+ add `ErrorComponent` to declarations & `ErrorService` to providers
-
in
errors/error.service.ts
:-
add
export class ErrorService
-
add
import { EventEmitter }
-
import
import { Error }
-
extract
error.title
from each error that is set up via the backend inroutes/messages.js
which always has an error object as well as a title object -
- extract
error.message
from each error that is set up via the backend ..........
- extract
-
now we are able to subscribe to
handleError
event in other places throughout app
-
-
in
app.module.ts
:-
add
errorService
to list array of providers -
import
import { ErrorService }
//enables application wide use
-
-
in
errors/error.component.ts
:-
add
implements OnInit
...listen to emitted events/errors -
import
OnInit
-
inject
ErrorService
in constructor -
implement
ngOnInit()
..subscribe to errors/ErrorService
-
-
in
messages/message.service.ts
:-
emit/submit error(s):
-
inject
errorService
via the constructor...possible because@Injectable()
has been added -
import
errorService
-
add
errorService
tocatch()
NOTE;catch
function allows for injection of custom error code before continuing on with default error flow viareturn Observable
-
copy paste same
.catch()
referenced above and use for every catch method that follows
-
-
in
auth/auth.service.ts
:-
import + inject
errorService
via the constructor -
copy paste same
.catch()
referenced above and use for every catch method that follows
-
-
in
erros/error.component.html
:- remove
fade
class
- remove
-
refresh and test errorService ..trying submitting message w/out auth
-
Lazy loading = load those modules/services on demand. ..decreases the startup time.
-
Creating a message module:
-
in
assets/app/messages
:-
create new file
message.module.ts
-
import
import { NgModule }
-
export class
Message
-
declare all components which are only used w/
Message
component -
import
MessagesComponent, MessageListComponent, MessageComponent, MessageInputComponent
in declaration instead of in app.module.ts
-
-
https://www.udemy.com/angular-2-and-nodejs-the-practical-guide/learn/v4/t/lecture/5867130?start=0
- import:
- add `CommonModule` to `imports` array
- import `import { NgModule } from '@angular/common';`
- add `FormsModule` to `imports` array
- import `import { FormsModule } from '@angular/forms';`
- add `providers` array with `MessageService`
- `import { MessageService } from './message.service';`
` import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { MessagesComponent } from "./messages.component";
import { MessageListComponent } from "./message-list.component";
import { MessageComponent } from "./message.component";
import { MessageInputComponent } from "./message-input.component"; `
-
in
app.module.ts
:-
[AuthService, ErrorService]
= global services so do not modularize them, howevermessageService
inapp/app.component.ts
is only used within messageService, so removemessageService
fromapp/app.component.ts
+ strip out allmessage-related components
fromapp.module.ts
and instead outsource service and instead contain them withinmessages.module.ts
-
delete
FormsModule
from imports -
add new import
MessageModule
-
import { MessageModule } from "./messages/message.module";
-
https://www.udemy.com/angular-2-and-nodejs-the-practical-guide/learn/v4/t/lecture/5867130?start=0
-
Only load Auth components if they are needed
-
in
assets/app/auth
:-
create
auth.module.ts
-
add components that are only used for auth
-
-
need to change routes for
auth
to enable lazy Loading-
rename
auth/auth.routes.ts
toauth/auth.routing.ts
+ importRouterModule
+export const authRouting
w/forChild(AUTH_ROUTES)
(means 'part' of application, not 'entire' app...) method + register routes that are only used for auth purposes -
in
app.routing.ts
useforRoot()
method to register root routes fir entire application-
remove
import { AUTH_ROUTES } from "./auth/auth.routes";
-
change
children: AUTH_ROUTES
toloadChildren: './auth/auth.module#AuthModule'
//tells Angular to lazy load.... -
also need to specify name of class that is to be imported by appending #AuthModule (as seen above)
-
-
in
auth/auth.module.ts
importauthRouting
-
-
Test lazy loading:
-
reload app, open dev tools, go to network tab, disable cache, reload app via
localhost:3000/messages
-
click on
authentication
tab (while watching network tab) to see associated chunk/file that has now been downloaded
-
- Build for production:
` npm run build:prod `//script in package.json that uses 'offline compilation'
- will now notice in network tab that file size is much smaller , g-zipping app will compress app even more
- Dive deep into Angular 2 Modules: https://angular.io/docs/ts/latest/guide/ngmodule.html
-
Create repo on Github
git init
git add .
git commit -m "first commit"
git remote add origin .......
git push -u origin master
-
Deploy to Heroku:
-
add
variables.env
to.gitigore
BEFORE deploying -
heroku create angular-udemy-deploy
//or whatever else you choose to name your app -
initialize existing (Github) repo on Heroku from project directory:
-
heroku git:remote -a angular-udemy-deploy
-
Create MongoDB/mLab addon
-
heroku addons:create mongolab
-
open new DB just created on mLab
-
heroku addons:open mongolab
//automatically creates config variable via Heroku UI -
heroku config:get MONGODB_URI
- set in
variables.env
file for local development
- set in
-
-
Set Environment to production
heroku config:add NODE_ENV=production
//auto populates value in config vars via Heroku UI
-
Add Node.js buildpack via Heroku site
-
Create Procfile to tell Heroku where app starts
-
Update URLs in
assets/app/messages/message.service.ts
:-
change from
http://localhost:3000/message
toapp domain
located in settings UI on Heroku -
repeat same in
assets/app/auth/authservice.ts
, with the exception of the final path beinguser
instead ofmessage
-
refactor later
-
add this to
package.json
:"engines": { "node": ">= 7.6.0" }
-
remove
/public/js/
from.gitignore
-
npm run build:prod
-
-
Update on Heroku:
-
NOTE: Any additional environment variables must be set up via Heroku UI via
Settings/Config Vars
-
git add .
-
git commit -m "make it better"
-
git push heroku master
-
Get live stream of the build errors:
-
heroku logs --tail
-
To to view logs:
-
run
heroku logs
-
heroku open
-
-
- ` nsp check `
https://securityheaders.io/
- ` snyk test `
- Run `snyk wizard` to explore remediation options
-
Go to https://aws.amazon.com/ ...login
-
Use free tier
-
go to
services/Elastic Beanstalk
-
go to
Create New Application
+ create app name + description -
next
+Create web server
-
Choose
node.js
+next
-
Leave
Sample application
checked +next
-
...
next
-
...
next
-
Select
t2.micro
+next
...part of free tier -
...
next
-
...
next
-
Copy Environment URL from Review page
-
Click
Launch
-
Go to
assets/app/messages/message.service.ts
and replace all URL paths with app URL -
Do the same in
assets/app/auth/auth.service.ts
-
Re-name
app.js
file toapplication.js
, but first create new Git branch for AWS Elastic Beanstalk