Angular Security - Authentication with JWTs

Angular Security - Authentication with JWTs

JSON Web Tokens (JWTs) help get around the contradiction between HTTP and authentication. In this post, we discuss how to use JWTs and an Angular client to add user authentication to your Node and Express application.

In this post, I will show you how to implement JWT authentication using a Node and Express server with a client written with Angular. You will see that, even though the concept is simple, the implementation requires knowledge of security best practices. The example given here is not complete and lacks a number of features required by a production server. In the next section, I will show you that Okta provides a simple and elegant solution to these shortcomings.

I will assume that you have some knowledge of JavaScript and that you have installed Node and the npm command line tool on your server.

Build a JWT Authentication Server

To start implementing the server that authenticates users using JSON Web Tokens, open a terminal and create a directory that will contain the server application, I have called my directory jwt-server. Navigate into that directory and run the following command to initialize your project.

Shell

npm init -y

You will need a number of packages to implement the server. Install then by running this command.

Shell

npm install --E [email protected] [email protected] [email protected] [email protected] [email protected] \
  [email protected] [email protected] [email protected]

I will explain each of these libraries when they appear in the code. Open up your favorite text editor and create a new file index.js with the following content.

JavaScript

const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const bearerToken = require('express-bearer-token');
const profile = require('./profile');
const port = process.env.PORT || 10101;
const app = express()
  .use(cors())
  .use(bodyParser.json())
  .use(bearerToken());
app.use('/', profile);
app.listen(port, () => {
  console.log(`Express server listening on port ${port}`);
});

This is the main server application. It first creates an express server that is used to listen to incoming HTTP requests and lets you register callback functions that generate responses to those requests. The server uses a number of middlewares that extend the behavior of the express server. The cors middleware allows the server to respond to Cross-Origin Requests. Body-parser is needed to parse the HTTP request body and create an object that is attached to the request data. Similarly, express-bearer-token extracts a bearer token from the request header and makes it available through the request object.

The express application attaches a router to the main route /. This router is defined in a separate file called profile.js. The first route that you will be implementing in this file lets a user register an account.

JavaScript

const express = require('express');
const bcrypt = require('bcryptjs');
const sqlite3 = require('sqlite3').verbose();
const db = new sqlite3.Database(':memory:');
db.serialize(() => {
  db.run("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT, password TEXT)");
});
const router = express.Router();
router.post('/register', function(req, res) {
  var hashedPassword = bcrypt.hashSync(req.body.password, 8);
  db.run("INSERT INTO users (name, email, password) "
        + "VALUES (?, ?, ?)", req.body.name, req.body.email, hashedPassword,
  function (err) {
    if (err) return res.status(500).send("An error occurred during registration");
    res.status(200).send({ status: 'ok' });
  });
});

module.exports = router;

I am using sqlite3 to simulate a user database. In this example, the database is purely held in memory. This means that all data will be lost when the server is stopped. In a production server, you should replace this with a proper SQL or NoSQL database.

When a user registers, their password is hashed using the bcryptjs library. Only the hashed password is stored in the database. On success, the server responds with an ok status. Once a user is registered they need to be able to log on. This can be done in a separate route /login. This is where you will start using JSON Web Tokens. Before you start implementing, create a file config.js that will store the secret for creating web tokens.

JavaScript

module.exports = {
  'secret': 'my_special_secret'
};

Next, add the require statement for njwt and the new config.js to profile.js.

JavaScript

const nJwt = require('njwt');
const config = require('./config');

Then, create the /login route in the same file.

JavaScript

router.post('/login', function(req, res) {
  db.get("SELECT id, name, email, password FROM users " 
        + "WHERE email=?", req.body.email, function (err, user) {
    if (err) return res.status(500).send({status: 'Server error', err:err});
    if (!user) return res.status(404).send('User not found');
    if (!bcrypt.compareSync(req.body.password, user.password)) {
      return res.status(401).send({ auth: false, token: null });
    }
    var jwt = nJwt.create({ id: user.id }, config.secret);
    jwt.setExpiration(new Date().getTime() + (24*60*60*1000));
    res.status(200).send({ auth: true, token: jwt.compact() });
  });
});

This route expects two parameters, email and password. The first step is to search in the database for the user’s email and obtain the user’s record. Then, bcrypt is used to compare the user’s password to the hashed password. If successful, a jwt is used to create a token that stores the user’s ID. The token is then sent back to the client in the response.

When a client attempts to access a restricted resource, it needs to send the token in the request header. The server then needs to authenticate the token. You can write an express middleware that performs this authentication task. Create a new file auth.js with the following content.

JavaScript

const nJwt = require('njwt');
var config = require('./config');
function jwtAuth(req, res, next) {
  if (!req.token) {
    return res.status(403).send({ auth: false, message: 'No token provided' });
  }
  nJwt.verify(req.token, config.secret, function(err, decoded) {
    if (err) {
      return res.status(500).send({ auth: false, message: 'Could not authenticate token' });
    }
    req.userId = decoded.body.id;
    next();
  });
}

module.exports = jwtAuth;

Remember the express-bearer-token middleware which extracts the JWT token from the request and places makes it available through req.tokenjwt.verify is used to check whether the token is valid or not. This function also extracts the user ID that was stored in the token and allows you to attach it to the request object.

All this now allows you to create a route that is protected and only available to users that are logged in. Open profile.js again and add the following.

JavaScript

const jwtAuth = require('./auth');
router.get('/profile', jwtAuth, function(req, res, next) {
  db.get("SELECT id, name, email FROM users WHERE id=?", req.userId, function (err, user) {
    if (err) {
      return res.status(500).send("There was a problem finding the user.");
    }
    if (!user) {
      return res.status(404).send("No user found.");
    }
    res.status(200).send(user);
  });
});

The /profile route simply returns the user’s profile information. See how the jwtAuth function is added to the /profile route as middleware. This protects the route. It also allows the handler callback to use the req.userId property to look up the user from the database. To test the server, add the following line to the scripts section of package.json.

JSON

 "start": "nodemon server.js", 

You can now run the server with this command.

Shell

npm start

This concludes the simple example of a server that uses JSON Web Tokens for authentication. Next, it is time to implement a client that accesses this server.

Add an Angular Client With JWT Authentication

I will be using Angular to implement the client. First, make sure you have the latest version of the Angular command line tool installed. You might have to run the following command using sudo, depending on your system.

Shell

npm install -g @angular/[email protected]

Navigate to a directory of your choice and create a new project for the client.

Shell

ng new jwt-client --routing --style=css 

Navigate into this folder and install the libraries for the Foundation responsive CSS framework.

Shell

npm install -E [email protected] [email protected]

Open src/styles.css and paste in the imports for the Foundation styles.

CSS

@import '~foundation-sites/dist/css/foundation.min.css';
@import '~ngx-foundation/dist/css/ngx-foundation.min.css';

Start by creating a service for communicating with the Node/Express server.

Shell

ng generate service server

Open the file src/app/server.service.ts and replace its contents with the following code.

TypeScript

import { Injectable } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
const baseUrl = 'http://localhost:10101';
@Injectable({
  providedIn: 'root'
})
export class ServerService {
  private loggedIn = false;
  private token: string;
  constructor(private http: HttpClient) {}
  setLoggedIn(loggedIn: boolean, token?: string) {
    this.loggedIn = loggedIn;
    this.token = token;
  }
  request(method: string, route: string, data?: any) {
    if (method === 'GET') {
      return this.get(route, data);
    }
    const header = (this.loggedIn) ? { Authorization: `Bearer ${this.token}` } : undefined;
    return this.http.request(method, baseUrl + route, {
      body: data,
      responseType: 'json',
      observe: 'body',
      headers: header
    });
  }
  get(route: string, data?: any) {
    const header = (this.loggedIn) ? { Authorization: `Bearer ${this.token}` } : undefined;
    let params = new HttpParams();
    if (data !== undefined) {
      Object.getOwnPropertyNames(data).forEach(key => {
        params = params.set(key, data[key]);
      });
    }
    return this.http.get(baseUrl + route, {
      responseType: 'json',
      headers: header,
      params
    });
  }
}

This service provides functions for posting requests to the server and obtaining the data. One important task of this service is to store the JWT token and add it to the request header. Another service will be in charge of authenticating with the server and obtaining the token. Create this service using the command line.

Shell

ng generate service auth

Fill the newly generated file src/app/auth.service.ts with this code.

TypeScript

import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { BehaviorSubject } from 'rxjs';
import { ServerService } from './server.service';
@Injectable()
export class AuthService {
  private loggedIn = new BehaviorSubject(false);
  private token: string;
  get isLoggedIn() {
    return this.loggedIn.asObservable();
  }
  constructor(private router: Router, private server: ServerService) {
    console.log('Auth Service');
    const userData = localStorage.getItem('user');
    if (userData) {
      console.log('Logged in from memory');
      const user = JSON.parse(userData);
      this.token = user.token;
      this.server.setLoggedIn(true, this.token);
      this.loggedIn.next(true);
    }
  }
  login(user) {
    if (user.email !== '' && user.password !== '' ) {
      return this.server.request('POST', '/login', {
        email: user.email,
        password: user.password
      }).subscribe((response: any) => {
        if (response.auth === true && response.token !== undefined) {
          this.token = response.token;
          this.server.setLoggedIn(true, this.token);
          this.loggedIn.next(true);
          const userData = {
            token: this.token,
          };
          localStorage.setItem('user', JSON.stringify(userData));
          this.router.navigateByUrl('/profile');
        }
      });
    }
  }
  logout() {
    this.server.setLoggedIn(false);
    delete this.token;
    this.loggedIn.next(false);
    localStorage.clear();
    this.router.navigate(['/']);
  }
}

This service takes care of authenticating the user and, when successful, storing the token in the browser’s local storage as well as notifying the ServerService of the token. You can now use AuthService in your application component. Open src/app/app.component.ts and paste the following content.

TypeScript

import { Component } from '@angular/core';
import { AuthService } from './auth.service';
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'jwt-client';
  constructor(private authService: AuthService) {}
  onLogout() {
    this.authService.logout();
  }
}

Change the application component in src/app/app.component.html to contain a top bar that is only visible when the user is logged in.

HTML

<div class="top-bar" *ngIf="authService.isLoggedIn | async as isLoggedIn">
  <div class="top-bar-left">
    <a class="logo" routerLink="/">MyApp</a>
  </div>
  <div class="top-bar-right show-for-medium">
    <ul class="menu">
      <li><a  routerLink="/profile">Profile</a></li>
      <li><a (click)="onLogout()">Logout</a></li>
    </ul>
 </div>
</div>
<router-outlet></router-outlet>

Next, create a component that allows a user to register a new user.

Shell

ng generate component register

Open src/app/register/register.component.ts and create a component that contains a registration form that can be submitted to the server.

TypeScript

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { ServerService } from '../server.service';
@Component({
  selector: 'app-login',
  templateUrl: './register.component.html',
  styleUrls: ['./register.component.css']
})
export class RegisterComponent implements OnInit {
  form: FormGroup;
  constructor(
    private fb: FormBuilder,
    private server: ServerService,
    private router: Router
  ) {}
  ngOnInit() {
    this.form = this.fb.group({
      email: ['', Validators.email],
      name: ['', Validators.required],
      password: ['', Validators.compose([Validators.required, Validators.minLength(8)])]
    },);
  }
  onSubmit() {
    console.log('Submitting');
    if (!this.form.valid) {
      console.log('Form not valid. Please check that fields are correctly filled in');
      return;
    }
    console.log('Form valid');
    const request = this.server.request('POST', '/register', {
      email: this.form.get('email').value,
      name: this.form.get('name').value,
      password: this.form.get('password').value
    });
    request.subscribe(() => {
      this.router.navigate(['/login']);
    })
  }
}

Note that the user is not logged in after registration. For this reason, the user is redirected to the login route when the registration was successful. The template for this component goes into src/app/register/register.component.html.

HTML

<div class="grid-container">
  <div class="grid-x">
    <div class="small-12 medium-10 medium-offset-1 large-8 large-offset-2 cell">
      <form [formGroup]="form" (ngSubmit)="onSubmit()">
        <h2>Sign Up</h2>
        <p>Please enter your details</p>
        <label class="full-width-input">
          Email
          <input type="text" placeholder="Email" formControlName="email" required>
        </label>
        <label class="full-width-input">
          Name
          <input type="text" placeholder="Name" formControlName="name" required>
        </label>
        <label class="full-width-input">
          Password
          <input type="password" placeholder="Password" formControlName="password" required>
        </label>
        <button class="button">Register</button>
      </form>
      <div class="login-link">
        Already registered? <a routerLink="/login">Login Here!</a>
      </div>
    </div>
  </div>
</div>

Creating the component for logging in follows the same steps.

Shell

ng generate component login

In src/app/login/login.component.ts, create the logic for showing the form and using AuthService to log in.

TypeScript

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { AuthService } from '../auth.service';
@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
  form: FormGroup;
  public loginInvalid: boolean;
  private formSubmitAttempt: boolean;
  constructor(private fb: FormBuilder, private authService: AuthService) {
  }
  ngOnInit() {
    this.form = this.fb.group({
      email: ['', Validators.email],
      password: ['', Validators.required]
    });
  }
  async onSubmit() {
    this.loginInvalid = false;
    this.formSubmitAttempt = false;
    if (this.form.valid) {
      try {
        await this.authService.login(this.form.value);      
      } catch (err) {
        this.loginInvalid = true;
      }
    } else {
      this.formSubmitAttempt = true;
    }
  }
}

The template src/app/login/login.component.html contains the HTML form for user email and password.

HTML

<div class="grid-container">
  <div class="grid-x">
    <div class="small-12 medium-10 medium-offset-1 large-8 large-offset-2 cell">
      <form [formGroup]="form" (ngSubmit)="onSubmit()">
        <h2>Log In</h2>
        <p>Please login to continue</p>
        <label class="full-width-input">
          Email
          <input type="text" placeholder="Email" formControlName="email" required>
        </label>
        <label class="full-width-input">
          Password
          <input type="password" placeholder="Password" formControlName="password" required>
        </label>
        <button class="button">Login</button>
      </form>
      <div class="register-link">
        Not yet registered? <a routerLink="/register">Register Now</a>
      </div>
    </div>
  </div>
</div>

Finally, you need a route for showing the user’s profile.

Shell

ng generate component profile

Copy the code below into src/app/profile/profile.component.ts. This component simply gets the profile data from the server and stores it for display.

TypeScript

import { Component, OnInit } from '@angular/core';
import { ServerService } from '../server.service';
@Component({
  selector: 'app-profile',
  templateUrl: './profile.component.html',
  styleUrls: ['./profile.component.css']
})
export class ProfileComponent implements OnInit {
  name: string;
  email: string;
  constructor(private server: ServerService) { }
  ngOnInit() {
    this.server.request('GET', '/profile').subscribe((user: any) => {
      if (user) {
        this.name = user.name;
        this.email = user.email;
      }
    });
  }
}

The template in src/app/profile/profile.component.html simply displays the result.

HTML

<div class="grid-container">
  <div class="grid-x">
    <div class="small-12 medium-10 medium-offset-1 large-8 large-offset-2 cell">
      <h2>Profile</h2>
      <h3>Name</h3>
      <p>
        {{name}}
      </p>
      <h3>Email</h3>
      <p>
        {{email}}
      </p>
    </div>
  </div>
</div>

OK, now I’ve thrown a lot of code at you. But it’s all quite simple really. The first two components display a form to the user, and, when submitted, the data is sent to the server. The last component acquires data from the server and display it. To make the whole thing work, some modules need to be imported. Open src/app/app.module.ts and add the following imports.

TypeScript

import { HttpClientModule } from '@angular/common/http';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AuthService } from './auth.service';

Then, add the following to the imports array.

TypeScript

@NgModule({
  ...
  imports: [
    ..
    HttpClientModule,
    FormsModule,
    ReactiveFormsModule  
  ],
  ...
})

Finally, add AuthService to the providers array.

TypeScript

@NgModule({
  ...
  providers: [AuthService],
  bootstrap: [AppComponent]
})

The last thing left to do is to register the component with the router. Open src/app/app-routing.module.ts and replace its content with the following code.

TypeScript

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { LoginComponent } from './login/login.component';
import { RegisterComponent } from './register/register.component';
import { ProfileComponent } from './profile/profile.component';
const routes: Routes = [
  { path: '', component: RegisterComponent },
  { path: 'login', component: LoginComponent },
  { path: 'register', component: RegisterComponent },
  { path: 'profile', component: ProfileComponent },
];
@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Your client is ready to try out. Start it by running the following command.

Shell

ng serve -o

The client allows a user to register, then log in, and view their profile data. Is this all there is to know about JSON Web Tokens? No, I have not covered a number of issues. In terms of user experience, it would be nice if the /profile route could be protected in the client. Currently, a user that is not logged in to the server can still navigate to the /profile route. The server will refuse to send any data, so an empty page will be presented.

Another big topic that I have completely avoided covers token expiration and refreshing tokens when a user interacts with a website. Both are necessary to guarantee security while also providing a good user experience.

Build Secure JWT Authentication in Angular and Express

Okta provides authentication services that can be easily integrated into your application. The Okta service is based on JWT, and it takes care of all the issues related to security and user experience. You don’t need to store passwords, generate tokens yourself, or think about automatically refreshing them. To start off, you will need a developer account with Okta.

In your browser, navigate to developer.okta.com, click on Create Free Account, and enter your details. You will receive an activation email to finish creating your account. Once you are done, you will be taken to your developer dashboard. Click on the Add Application button to create a new application. Start by creating a new single-page application. Choose Single Page App and click Next.

Creating a new aplication

On the next page, you will need to edit the default settings. Make sure that the port number is 4200. This is the default port for Angular applications. Then click Done.

Adding application credentials

That’s it. You should now see a Client ID which you will need to paste into your JavaScript code.

Express Server for Authentication

The server that uses authentication using the Okta service does not need to implement any user registration or login. Registration is, of course, useful to keep track of user data, but it is not strictly necessary. Create a new directory called okta-server and run npm init -y in it as with the jwt-server. The libraries needed are slightly different.

Shell

npm install -E [email protected] [email protected] [email protected] \
  @okta/[email protected] [email protected] [email protected]

The main application file index.js is the same as jwt-server/index.js. The authentication middleware auth.js looks slightly different because it now uses Okta.

JavaScript

const OktaJwtVerifier = require('@okta/jwt-verifier');
const oktaJwtVerifier = new OktaJwtVerifier({
  issuer: 'https://{yourOktaDomain}/oauth2/default',
  clientId: '{yourClientId}'
});
function oktaAuth(req, res, next) {
  if (!req.token) {
    return res.status(403).send({ auth: false, message: 'No token provided' });
  }
  oktaJwtVerifier.verifyAccessToken(req.token).then(function(jwt) {
    req.userId = jwt.claims.uid;
    req.userEmail = jwt.claims.sub;
    next();
  }, function(err) {
    return res.status(500).send({ auth: false, message: 'Could not authenticate token' });
  });
}
module.exports = oktaAuth;

Here, {yourClientId} is the client ID from the application that you created earlier in the Okta dashboard. The router implementation in profile.js only contains a single route. I have removed the /register and /login routes and only kept the /profile route.

JavaScript

var express = require('express');
var oktaAuth = require('./auth');
var router = express.Router();
router.get('/profile', oktaAuth, function(req, res, next) {
  console.log('ME', req.userId);
  res.status(200).send({id: req.userId, email: req.userEmail});
});
module.exports = router;

This route returns the data contained in the token. You could choose to use a database to store additional data and send it to the client, but I want to show you here that this is not required.

Add the following line to the scripts section of package.json.

JSON

"start": "nodemon server.js",

Start the server with npm start.

Single Sign-On for Your Angular Client

Start off in the same way as creating the jwt-client application, but call it okta-client.

Shell

ng new okta-client --routing --style=css  

Install foundation-sites and ngx-foundation, and then edit src/style.css and src/app/app.component.html in the same way, as with the Angular client in the previous section.

Shell

npm install -E [email protected] [email protected]

Edit src/styles.css and paste in the imports for the Foundation styles.

CSS

@import '~foundation-sites/dist/css/foundation.min.css';
@import '~ngx-foundation/dist/css/ngx-foundation.min.css';

Copy src/app/app.component.html from jwt-client to okta-client.

In src/app/app.component.html, on the first line, change *ngIf="authService.isLoggedIn | async as isLoggedIn" to *ngIf="isLoggedIn
| async as isLoggedIn".

HTML

div class="top-bar" *ngIf="isLoggedIn | async as isLoggedIn">

Next, install the Okta packages.

Shell

npm install -E @okta/[email protected] @okta/[email protected]

Just as before, create a server service.

Shell

ng generate service server

The implementation of the service in src/app/server.service.ts is very similar to the previous client. The only difference is that the JWT token is obtained through the OktaAuthService.

TypeScript

import { Injectable } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { OktaAuthService } from '@okta/okta-angular';
import { Subject } from 'rxjs';
const baseUrl = 'http://localhost:10101';
@Injectable({
  providedIn: 'root'
})
export class ServerService {
  constructor(public oktaAuth: OktaAuthService, private http: HttpClient) {
  }
  request(method: string, route: string, data?: any) {
    if (method === 'GET') {
      return this.get(route, data);
    }
    const subject = new Subject();
    this.oktaAuth.getAccessToken().then((token) => {
      const header = (token) ? {Authorization: `Bearer ${token}`} : undefined;
      const request = this.http.request(method, baseUrl + route, {
        body: data,
        responseType: 'json',
        observe: 'body',
        headers: header
      });
      request.subscribe(subject);
    });
    return subject;
  }
  get(route: string, data?: any) {
    const subject = new Subject();
    this.oktaAuth.getAccessToken().then((token) => {
      const header = (token) ? {Authorization: `Bearer ${token}`} : undefined;
      let params = new HttpParams();
      if (data !== undefined) {
        Object.getOwnPropertyNames(data).forEach(key => {
          params = params.set(key, data[key]);
        });
      }
      const request = this.http.get(baseUrl + route, {
        responseType: 'json',
        headers: header,
        params
      });
      request.subscribe(subject);
    });
    return subject;
  }
}

The client still contains a login component, but in this case, it simply contains a widget provided by the @okta/okta-signin-widget library.

Shell

 ng generate component login --inlineStyle=true --inlineTemplate=true 

Modify the contents of src/app/login/login.component.ts so it looks like the following code:

TypeScript

import { Component, OnInit } from '@angular/core';
import { Router, NavigationStart} from '@angular/router';
import { OktaAuthService } from '@okta/okta-angular';
import * as OktaSignIn from '@okta/okta-signin-widget';
@Component({
  selector: 'app-login',
  template: `
    <div class="grid-container">
      <div class="grid-x">
        <div class="small-12 medium-10 medium-offset-1 large-8 large-offset-2 cell">
          <div id="okta-signin-container"></div>
        </div>
      </div>
    </div>
  styles: []
})
export class LoginComponent implements OnInit {
  widget = new OktaSignIn({
    baseUrl: 'https://{yourOktaDomain}'
  });
  constructor(private oktaAuth: OktaAuthService, router: Router) {
    // Show the widget when prompted, otherwise remove it from the DOM.
    router.events.forEach(event => {
      if (event instanceof NavigationStart) {
        switch (event.url) {
          case '/login':
          case '/profile':
            break;
          default:
            this.widget.remove();
            break;
        }
      }
    });
  }
  ngOnInit() {
    this.widget.renderEl({
      el: '#okta-signin-container'},
      (res) => {
        if (res.status === 'SUCCESS') {
          this.oktaAuth.loginRedirect('/profile', { sessionToken: res.session.token });
          // Hide the widget
          this.widget.hide();
        }
      },
      (err) => {
        throw err;
      }
    );
  }
}

Copy the jwt-client/src/app/profile directory into your okta-client project and change ProfileComponent to retrieve the name from Okta’s Angular SDK.

TypeScript

import { Component, OnInit } from '@angular/core';
import { ServerService } from '../server.service';
import { OktaAuthService } from '@okta/okta-angular';
@Component({
  selector: 'app-profile',
  templateUrl: './profile.component.html',
  styleUrls: ['./profile.component.css']
})
export class ProfileComponent implements OnInit {
  id: string;
  email: string;
  name: string;
  constructor(private server: ServerService, oktaAuth: OktaAuthService) { 
    oktaAuth.getUser().then(user => {
      this.name = user.name;
    })
  }
  ngOnInit() {
    this.server.request('GET', '/profile').subscribe((user: any) => {
      if (user) {
        this.id = user.id;
        this.email = user.email;
      }
    });
  }
}

Next, open src/app/app.module.ts and paste the following code into it.

TypeScript

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { OKTA_CONFIG, OktaAuthModule } from '@okta/okta-angular';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { LoginComponent } from './login/login.component';
import { ProfileComponent } from './profile/profile.component';
const oktaConfig = {
  issuer: 'https://{yourOktaDomain}/oauth2/default',
  redirectUri: 'http://localhost:4200/implicit/callback',
  clientId: '{yourClientId}',
  scope: 'openid profile'
};
@NgModule({
  declarations: [
    AppComponent,
    LoginComponent,
    ProfileComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule,
    FormsModule,
    ReactiveFormsModule,
    OktaAuthModule
  ],
  providers: [{ provide: OKTA_CONFIG, useValue: oktaConfig }],
  bootstrap: [AppComponent]
})
export class AppModule { }

All that is left to do now is to register the components with the router in src/app/app-routing.module.ts.

TypeScript

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { OktaCallbackComponent, OktaAuthGuard } from '@okta/okta-angular';
import { LoginComponent } from './login/login.component';
import { ProfileComponent } from './profile/profile.component';
export function onAuthRequired({ oktaAuth, router }) {
  router.navigate(['/login']);
}
const routes: Routes = [
  { path: '', component: ProfileComponent, canActivate: [OktaAuthGuard], data: { onAuthRequired }  },
  { path: 'login', component: LoginComponent },
  { path: 'profile', component: ProfileComponent, canActivate: [OktaAuthGuard], data: { onAuthRequired }  },
  { path: 'implicit/callback', component: OktaCallbackComponent }
];
@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Finally, open src/app/app.component.ts and replace its contents with the following code.

TypeScript

import { Component, OnInit } from '@angular/core';
import { OktaAuthService } from '@okta/okta-angular';
import { BehaviorSubject } from 'rxjs';
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  title = 'okta-client';
  isLoggedIn = new BehaviorSubject(false);
  constructor(public oktaAuth: OktaAuthService) {
    this.oktaAuth.$authenticationState.subscribe(this.isLoggedIn);
  }
  ngOnInit() {
    this.oktaAuth.isAuthenticated().then((auth) => {this.isLoggedIn.next(auth)});
  }
  onLogout() {
    this.oktaAuth.logout('/');
  }
}

Your Angular app now implements authentication using Okta and JWTs! It guards the routes that should be accessed and automatically redirects the user to the login page when they are not logged in. In contrast to the example in the previous section, the implementation in this section is complete. The Okta libraries take care of all remaining issues that were not covered by the bare-bones JWT implementation.

You can test the client by running the ng serve command as usual. Enter valid credentials when prompted.

User sign in in Angular client

Once logged in, you will be redirected to the profile page and you’ll see your user information, just like before.

User successfully logged in

Learn More About Angular and JWTs

I hope that, in this tutorial, I have given you some insight into JSON Web Tokens and their uses. They solve some of the problems faced by traditional session based authentication by encrypting the user information and passing it back to the client. I have shown you how to implement a server and client using JWT. This example showed you the basics but, in order to be used for a production server, additional steps would need to be taken. Okta simplifies the task of creating token-based authentication. Using only a few steps you can implement a fully working server and client.

The code for this tutorial can be found on GitHub at here.

Thank you for reading !

Authentication in Angular & JWT

Authentication in Angular & JWT

Authentication in Angular & JWT. Angular makes it dead simple wire up authentication through mechanisms… ... I want to walk you through building some simple authentication in Angular using JWT as the authentication mechanism. ... Wrapping Up

Building a web-based product is not just a skill anymore; it’s a necessity, for anything and everything that requires public attention.

A decade into the digital revolution, we are finally witnessing a tectonic shift in users’ behavior towards consumer products, wherein a majority of these products are either web-applications or mobile-applications.

A web/mobile app is usually a culmination of many components working in unison. One of the most overlooked, but significant part is user authentication. Believe me, when I say this, 70% of users leave the platform if they aren’t satisfied with the on-boarding experience.

It HAS to be Smooth & Simple.

There are countless tutorials out there exhibiting code of how authentication in angular works, some even go the extra mile to explain how the code runs, but the only issue with such codes (for rookies) is the flowery words and jargon they use, which can be quite troublesome for someone who is just starting-out in the domain.

The following is a detailed, yet simple guide on how authentication works in angular. The code for the same could be found here. The following code is well commented so that you understand what is actually happening!

This tutorial is not a step by step guide to authentication with angular. The Internet is already swarming with thousands of them, but what’s missing is a detailed explanation of the working of those steps.

It is recommended that you clone the project here and go through this tutorial while referencing the code. It will give you the clarity you are yearning for…

Before we start with the “coding” section, we need to understand the basic concept behind authentication and JSON Web Tokens (or JWT as millennials call it).

Authentication is a means of verifying the identity of a user, which means if the user is allowed to access the application or not. The two most prevalent ways a user can get authenticated is either through social login (Google+/Facebook login) or through a registered username (can be an email) and password. We will be using the latter to understand the inner workings of authentication in angular.

Recommended Read: A Detailed Guide to CSS Animations and Transitions

The codebase for this tutorial works on the following components:

  1. JSON Web Token
  2. Client + localStorage
  3. Back-end + Secure API Endpoints + DB
  4. Authentication Service
  5. User Service
  6. JWT Interceptor
  7. Auth Guard
  8. Error Interceptor

After understanding the 8 elements mentioned above, you will not only learn about the workings of JWT and authentication but will also be able to appreciate the process behind it.

  1. JSON Web Tokens (JWT)

JSON Web Tokens, if defined abstractly, is a JSON object which helps in exchanging information between two or more parties securely. In layman terms, it’s a type of validation token from the authentication server, which indicates that the username and password supplied by the user at the time of logging in whether it is correct or not, and thus the user is “authenticated”.

Read more about JWT and its intricacies here.

2. Client + localStorage

The client here is the user operating on a browser (Google Chrome/Firefox/Opera/Safari). For every registered user, there is a set of username and password stored in the database at the backend.

The two most important types of storage properties of any browser are the localStorage and the sessionStorage; they allow you to store key-value pairs in the browser.

  • localStorage
    It is a persistent storage option when you don’t want the user to be logged out on refresh or closing of a browser tab. The localStorage stores the data without expiration date by default. It is a read-only property.
  • sessionStorage As the name indicates, it stores data for a particular session only. It is less persistent, and the user data gets lost on the closing of the browser tab or refreshing.

We will be using localStorage in our case.

NOTE: One can find localStorage in the developer console (F12 on Chrome) under the “Application” tab.

3. Backend + Secure API Endpoints + DB

The backend for this tutorial is a fake backend as we just want to understand how things are working on the front-end part. But this backend works as any real backend wherein it has API’s that provide us with a response body and response status. The fake-backend.ts also has test data (DB) for a test user. The credentials input by the user will be validated against this test data.

Test Data:

The fake-backend will serve as a standalone backend with a delayed response so that it can imitate a real backend. The two major tasks it will perform are:

  1. It will check the user credentials that come from “Authentication Service” during login
  2. It will also work as a fake database keeping the user details (hitting a secure API end-point). The user can request for the user details only when the requests have valid JWT Token in its request authorization header

The two API’s that our current backend has are:

  • To check credentials — **/users/authenticate**If the user credentials match the test user stored data, the backend sends a response body with a JWT token.

fake-backend.ts

  • To give back user details — /users This is a SECURE API endpoint. By secure, it means that to access this API endpoint, the request must have a valid JWT Token with it. This token is in the request header with the “Authorization: Bearer JWT-TOKEN” property.It first checks for a valid JWT token and then it responds accordingly.

fake-backend.ts

4. Authentication Service

Authentication service is used to LOG IN and LOG OUT from the application. It possesses the credentials (username and password) from the client (user/browser) to the backend and checks for the response, if it has JWT token.

If the response from the backend has a JWT token, then the authentication was successful. On successful authentication, the user details are stored in the local storage along with JWT token.

5. User Service

The user service is a service dedicated to hitting the 2nd API (the secure API end-point) to fetch the data for all the users present in the database. Currently, only a single user is present — test user. This User Service is accessed via the home component as soon as the user logs in and the home component is initialized.

  • home.component.ts

The service will get the response from the backend only if the backend receives the request along with a valid JWT token.

  • user.service.ts

The token is not sent when the request by the client is made. Here is when JWT Interceptor comes to rescue!

6. JWT Interceptor

The JWT interceptor intercepts the incoming requests from the application/user and adds JWT token to the request’s Authorization header, only if the user is logged in (because then the JWT token will be present in the localStorage at client’s end).

The JWT interceptor works very smarty by not modifying the original request made by the user service, but cloning the request and adding the JWT token to the request header and then forwarding the cloned request to the backend. The value of the token is fetched from the client’s localStorage. If the user is logged out, there will be no JWT token in the localStorage, and thus, the user won’t be able to access the secured API endpoint.

jwt.interceptor.ts

7. Auth Guard

The Auth Guard works as umm — a Guard!

The auth guard ensures that only authenticated users access specific routes in the application. The auth guard implements an interface called CanActivate with the help of which it returns either true or false.

True If the user is logged in, i.e. the localStorage has current users’ details along with a valid JWT token

False If the user is logged out, i.e. the localStorage lacks the current user details along with a valid JWT token, and in turn, will then redirect the user to the login page.

auth.guard.ts

In this particular example, we are using the presence of the current user detail as a way to give rights to access one specific route, i.e. ‘/’ (root), or the HomeComponent. There can be other conditions too like role-based authentication. The rights to access particular routes will then be allocated on the basis of the role of a specific user. The user will be able to access the admin route (say) only if he is an admin.

Even though it is developed in a separate file auth.guard.ts, but it is implemented in the routing of the application, with the canActivate parameter.

app.routing.ts

8. Error Interceptor

The HTTP error interceptor works with the calling service and the API’s

It intercepts the responses from the API and checks for the status codes (if there were any errors).

Error Status

  1. 200: Success
  2. 401: Unauthorized Response — the user will be automatically logged out

All other errors are RE-THROWN, to be caught by the calling service so an alert can be displayed by the user service/component on the client’s end.

error.interceptor.ts

Wrapping Up

The thing about angular (or any framework) is, the more you work with it, and the more you’ll discover what all you did not know. The lack of knowledge about the fundamentals will prove to be a hindrance and in turn, will slow down the development process. This is my effort to make one of the components of a web-app development process, i.e. Authentication — EASY!

Recommended Reading

How do Timer in Angular

How to store HTTP calls using Angular and RxJS.

Content Projection in Angular for Developers

A complete Guide to the Angular CLI

Game Development with Angular: Tetris Game

Store user information with Angular 6, Nodejs and Express.

Store user information with Angular 6, Nodejs and Express.

Let’s create a very simple Angular form application that stores user information locally in memory on a server.

Let’s create a very simple Angular form application that stores user information locally in memory on a server.

On submitting the form, we’ll populate the data on the next page and add two additional elements to display the guid and customer uid.

The Portfolio API - Grow your coding career effortlessly | gitconnected

Getting Started

First we’ll need Node & NPM installed.

To check if you have node installed run this command in your terminal:

node -v

To confirm that you have npm installed you can run this command:

**npm -v**

If not, you can download & install NodeJS & NPM from https://nodejs.org/en/

Install the Angular CLI:

npm install -g @angular/cli

Generate a new Angular project:

ng new charts6

Navigate to http://localhost:4200/. At this point the application will automatically reload if you change any of the source files. The initial page should be the default Angular 6 page:

The main Application component will have a UI Router that initially shows the Input User Data Form component. On submitting the form**, Application** component will show the Display User Data component.

The Flow

Create components with ng cli

ng new angular-node-express
cd angular-node-express
ng generate component input-user-data-form --spec false

The CLI will create all the appropriate files and place them into the app folder

It should also add declarations into the app.module.ts

Next, we’ll add bootstrap 3 CSS library into the project to add styling.

npm install --save [email protected]

Afterwards add a path to “styles” array in the angular.json

"styles": [
              "node_modules/bootstrap/dist/css/bootstrap.min.css",
              "src/styles.css"
          ]

The Bootstrap grid system uses containers that hold rows and column. Rows and columns are percentage based. It is the container that changes responsively.

The container is used as a wrapper for the content.

We’ll choose a fluid container whose width will always be the width of the device.

fluid-container.html

<div class="container-fluid">

A grid row acts like a wrapper around the columns.

A row is created by adding the class row to an element inside the container.

fluid-container-row.html

<div class="container-fluid">
 <div class="row"></div>
</div>

Ideally, the number of columns equals 12 for every row. Different column class prefixes are used for different sized devices. Here 1 column takes up the size of 12 columns.

Change the app.component.html into the following:

app.component.html

<div class="container-fluid">
  <div class="row">
    <div class="col-xs-12">
      <h1>Hello World!</h1>
    </div>
  </div>
</div>
Running the application in DEV mode

Angular CLI uses webpack underneath to run the application on port 4200.

We’ll run our Node.js API endpoint on port 3000. But making an API call to another server (from port 4200) causes the CORS browser exception.

This is why we’ll use a proxy as a workround to the same-origin policy. The Proxy will re-route our API calls to a Node.js server to avoid the CORS exceptions. CORS is a browser security issue and does not apply to “backend to backend” communication.

All requests made to /api/... from within our application will be forwarded to [http://localhost:3000/api/...](http://localhost:3000/api/....)

With the proxy, our application diagram will look like this:

Create the proxy.conf.json in root folder

proxy.conf.json

{
  "/api/*": {
    "target": "http://localhost:3000",
    "secure": false,
    "logLevel": "debug",
    "changeOrigin": true
  }
}

When starting Angular & Proxy servers use the following command:

ng serve --proxy-config proxy.conf.json
Creating the Input User Data Component

First let's focus on creating UI and work on API endpoints thereafter

There are 3 rules you must follow when making a Bootstrap form:

  1. Every field must be wrapped in a div element
  2. Every should have .control-label class and its for value must match the corresponding <input>’s id, so when the label is clicked, the input becomes focused.
  3. Every should be given .form-control

Set the input-user-data-form.component.html to the following:

input-user-data-form.component.html

	<form>
	<div class="form-group"> <!-- First Name -->
		<label for="first_name" class="control-label">First Name</label>
		<input type="text" class="form-control" id="first_name" name="first_name" placeholder="Martin">
	</div>

	<div class="form-group"> <!-- Last Name -->
		<label for="last_name" class="control-label">Last Name</label>
		<input type="text" class="form-control" id="last_name" name="last_name" placeholder="Toha">
	</div>

	<div class="form-group"> <!-- Email -->
		<label for="email" class="control-label">E-mail</label>
		<input type="text" class="form-control" id="email" name="email" placeholder="[email protected]">
	</div>
							
	<div class="form-group"> <!-- Zip Code-->
		<label for="zipcode" class="control-label">Zip Code</label>
		<input type="text" class="form-control" id="zipcode" name="zipcode" placeholder="#####">
	</div>
	
	<div class="form-group"> <!-- Password -->
		<label for="password" class="control-label">Password</label>
		<input type="text" class="form-control" id="password" name="password" placeholder="*****">
	</div>
	
	<div class="form-group"> <!-- Register Button -->
		<button type="submit" class="btn btn-primary">Register</button>
	</div>

</form>

Change selector in the input-user-data-form.component.ts to input-user-data-form

And add it to app.component.html

Field Validation with Bootstrap

We are going to have to validate our fields prior to submitting the form to the backend. If a field is invalid, we can communicate it to the user by making the entire field red.

For each of the fields, we will:

Add the class .has-error to the .form-groupdiv

And create a <p>with the class .has-textto explain the problem

input-user-data-form.component-validation.html

<form>
	<div class="form-group has-error"> <!-- First Name -->
		<label for="first_name" class="control-label">First Name</label>
		<input type="text" class="form-control" id="first_name" name="first_name" placeholder="Martin">
		<p id="first_name_error" class="help-block">Invalid first name.</p>
	</div>

	<div class="form-group has-error"> <!-- Last Name -->
		<label for="last_name" class="control-label">Last Name</label>
		<input type="text" class="form-control" id="last_name" name="last_name" placeholder="Toha">
		<p id="last_name_error" class="help-block">Invalid last name.</p>
	</div>

	<div class="form-group has-error"> <!-- Email -->
		<label for="email" class="control-label">E-mail</label>
		<input type="text" class="form-control" id="email" name="email" placeholder="[email protected]">
		<p id="email_error" class="help-block">Invalid email address.</p>
	</div>
							
	<div class="form-group has-error"> <!-- Zip Code -->
		<label for="zipcode" class="control-label">Zip Code</label>
		<input type="text" class="form-control" id="zipcode" name="zipcode" placeholder="#####">
		<p id="zipcode_error" class="help-block">Invalid zip code.</p>
	</div>

	<div class="form-group has-error"> <!-- Password -->
		<label for="password" class="control-label">Password</label>
		<input type="text" class="form-control" id="password" name="password" placeholder="*****">
		<p id="password_error" class="help-block">Invalid password.</p>
	</div>
	
	<div class="form-group has-error"> <!-- Register Button -->
		<button type="submit" class="btn btn-primary">Register</button>
	</div>

</form>

Here is the form with errors added for every field:

Making a Reactive Form

A reactive form is a bit different from template-driven forms by providing more predictability with synchronous access to the data model, immutability with observable operators, and change tracking through observable streams.

To use the reactive form, we must import the FormsModule & ReactiveFormsModule from the @angular/forms package and add it to our app.module.ts

Next, lets modify the input-user-data-form.component.

We’ll inject the formBuilder into the component for use in the ngOnInit life cycle method. FormBuilder a service that helps making forms easier.

The FormBuilder will create a FormGroup userForm with FormControls. Each FormControl has a default value and a set of Validators.

Think of the userForm as a schema for the actual values. It holds validation rules for fields inside the form.

We’ll add registered & submitted boolean flags to the Component. We’ll use these to indicate the current form state**.**

Also, we’ll create a validator function for every input, see invalidFirstName(), invalidLastName(), invalidEmail(), invalidZipcode(), invalidPassword().

A validator function will be used in markup to hide / show error messages for each field.

input-user-data-form.components.ts

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, FormControl, Validators } from '@angular/forms';

@Component({
  selector: 'input-user-data-form',
  templateUrl: './input-user-data-form.component.html',
  styleUrls: ['./input-user-data-form.component.css']
})

export class InputUserDataFormComponent implements OnInit {
	registered = false;
	submitted = false;
	userForm: FormGroup;

  constructor(private formBuilder: FormBuilder)
  {

  }

  invalidFirstName()
  {
  	return (this.submitted && this.userForm.controls.first_name.errors != null);
  }

  invalidLastName()
  {
  	return (this.submitted && this.userForm.controls.last_name.errors != null);
  }

  invalidEmail()
  {
  	return (this.submitted && this.userForm.controls.email.errors != null);
  }

  invalidZipcode()
  {
  	return (this.submitted && this.userForm.controls.zipcode.errors != null);
  }

  invalidPassword()
  {
  	return (this.submitted && this.userForm.controls.password.errors != null);
  }

  ngOnInit()
  {
  	this.userForm = this.formBuilder.group({
  		first_name: ['', Validators.required],
  		last_name: ['', Validators.required],
  		email: ['', [Validators.required, Validators.email]],
  		zipcode: ['', [Validators.required, Validators.pattern('^[0-9]{5}(?:-[0-9]{4})?$')]],
  		password: ['', [Validators.required, Validators.minLength(5), Validators.pattern('^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[a-zA-Z0-9]+$')]],
  	});
  }

  onSubmit()
  {
  	this.submitted = true;

  	if(this.userForm.invalid == true)
  	{
  		return;
  	}
  	else
  	{
  		this.registered = true;
  	}
  }

};

In the input-user-data-form.component.html, and for every Reactive form we’ll use a directive [formGroup] to bind to userForm variable we defined in the component

We’ll bind the onSubmit handler via the (ngSubmit) directive.

To connect an HTML input element to a Reactive form we need to add formControlNameattribute to each of the form inputs.

We’ll use the *ngIf & [ngClass] directives to control error messages. The styles and validation markup will get displayed after user attempts to submit the form. This is controlled with [submitted] property of the component.

After submitting the form, it will be validated every time user changes the input value, causing errors to show & hide dynamically.

input-user-data-form.component-02.html

<form [formGroup]="userForm" (ngSubmit)="onSubmit()">

	<div class="form-group" [ngClass]="{ 'has-error': invalidFirstName() }"> <!-- First Name -->
		<label for="first_name" class="control-label">First Name</label>
		<input type="text" formControlName="first_name" class="form-control" id="first_name" name="first_name" placeholder="Martin">
		<p *ngIf="invalidFirstName()" id="first_name_error" class="help-block">Invalid first name.</p>
	</div>

	<div class="form-group" [ngClass]="{ 'has-error': invalidLastName() }"> <!-- Last Name -->
		<label for="last_name" class="control-label">Last Name</label>
		<input type="text" formControlName="last_name" class="form-control" id="last_name" name="last_name" placeholder="Toha">
		<p *ngIf="invalidLastName()" id="last_name_error" class="help-block">Invalid last name.</p>
	</div>

	<div class="form-group" [ngClass]="{ 'has-error': invalidEmail() }"> <!-- Email -->
		<label for="email" class="control-label">E-mail</label>
		<input type="text" formControlName="email" class="form-control" id="email" name="email" placeholder="[email protected]">
		<p *ngIf="invalidEmail()" id="email_error" class="help-block">Invalid email address.</p>
	</div>

	<div class="form-group" [ngClass]="{ 'has-error': invalidZipcode() }"> <!-- Zip Code-->
		<label for="zipcode" class="control-label">Zip Code</label>
		<input type="text" formControlName="zipcode" class="form-control" id="zipcode" name="zipcode" placeholder="#####">
		<p *ngIf="invalidZipcode()" id="zipcode_error" class="help-block">Invalid zip code.</p>
	</div>

	<div class="form-group" [ngClass]="{ 'has-error': invalidPassword() }"> <!-- Password -->
		<label for="password" class="control-label">Password</label>
		<input type="password" formControlName="password" class="form-control" id="password" name="password" placeholder="*****">
		<p *ngIf="invalidPassword()" id="password_error" class="help-block">Invalid password.</p>
	</div>
	
	<div class="form-group has-error"> <!-- Register Button -->
		<button type="submit" class="btn btn-primary">Register</button>
	</div>

</form>

Here is what the form looks like initially:

And the form after filling out some fields with error:

Looks good so far! Lets continue with the next component.

Creating the Display User Data Component

The next Angular component will display user data. This will be a simple component that visualizes a data model passed into it.

Lets create a data model UserInfoModel in app/models, we’ll make it deserialize an object when its passed into the constructor.

UserInfoModel.ts

export class UserInfoModel
{
	guid: string;
	customerUid: string;
	
	first_name: string;
	last_name: string;

	email: string;
	zipcode: string;

	password: string;

	constructor(obj: any = null)
	{
		if(obj != null)
		{
			Object.assign(this, obj);
		}
	}
}

The view display-user-data.component.html binds to the model via interpolation {{…}} and displays the information.

display-user-data.component.html

<legend>User Information</legend>
<div>
	<div class="form-group row"> <!-- GUD -->
		<label class="col-xs-6 col-form-label">GUID</label>
		<div class="col-xs-6">{{user.guid}}</div>
	</div>

	<div class="form-group row"> <!-- Customer UID -->
		<label class="col-xs-6 col-form-label">Customer UID</label>
		<div class="col-xs-6">{{user.customerUid}}</div>
	</div>

	<div class="form-group row"> <!-- First Name -->
		<label class="col-xs-6 col-form-label">First Name</label>
		<div class="col-xs-6">{{user.first_name}}</div>
	</div>

	<div class="form-group row"> <!-- Last Name -->
		<label class="col-xs-6 col-form-label">Last Name</label>
		<div class="col-xs-6">{{user.last_name}}</div>
	</div>

	<div class="form-group row"> <!-- E-mail -->
		<label class="col-xs-6 col-form-label">E-mail</label>
		<div class="col-xs-6">{{user.email}}</div>
	</div>

	<div class="form-group row"> <!-- Zip Code -->
		<label class="col-xs-6 col-form-label">Zip Code</label>
		<div class="col-xs-6">{{user.zipcode}}</div>
	</div>

	<div class="form-group row"> <!-- Password -->
		<label class="col-xs-6 col-form-label">Password</label>
		<div class="col-xs-6">{{user.password}}</div>
	</div>

</div>

And just for testing purposes, DisplayUserDataComponent class will assign some default values into the UserInfoModel

display-user-data.component.ts

import { Component, OnInit } from '@angular/core';
import {UserInfoModel} from '../models/userInfo'

@Component({
  selector: 'display-user-data',
  templateUrl: './display-user-data.component.html',
  styleUrls: ['./display-user-data.component.css']
})

export class DisplayUserDataComponent implements OnInit {
	
	user: UserInfoModel = new UserInfoModel({guid: "D21ds12x", 
		customerUid: "cust2dsa12dsa", 
		first_name: "John", 
		last_name: "Doe", 
		email: "[email protected]", 
		zipcode: 10283,
		password: "Idasn2x2#"});

	constructor() { }

	ngOnInit()
	{

	}

}


Both of the pages look great!

Now, lets connect the Router into the AppComponent to establish a page flow

Connecting Routes & RouterModule

Our goal with routing is to have the InputUserDataFormComponentrendered when the url is /, and DisplayUserDataComponentshown when the url is /user/:uid

First, lets setup the imports to @angular/router, then define an array routes of our paths and destination pages.

const routes: Routes = [
  {
    path: '',
    component: InputUserDataFormComponent
  },
  {
    path: 'user/:uid',
    component: DisplayUserDataComponent
  }
];

In the importsadd RouterModule.forRoot(routes)

app.module-w-routes.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { ReactiveFormsModule, FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { InputUserDataFormComponent } from './input-user-data-form/input-user-data-form.component';
import { DisplayUserDataComponent } from './display-user-data/display-user-data.component';
import { Routes, RouterModule } from "@angular/router";

const routes: Routes = [
  {
    path: '',
    component: InputUserDataFormComponent
  },
  {
    path: 'user/:uid',
    component: DisplayUserDataComponent
  }
];

@NgModule({
  declarations: [
    AppComponent,
    InputUserDataFormComponent,
    DisplayUserDataComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    ReactiveFormsModule,
    RouterModule.forRoot(routes)
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Place the router-outletin the app.component.html

app.component-wroute.html

<div class="container-fluid">
  <div class="row">
    <div class="col-xs-12">
      <router-outlet></router-outlet>
    </div>
  </div>
</div>

Now the initial “/” root page looks like this:

Type “/user/a01” in the address bar and you get the User Information page

All works as intended on the client! Now lets continue with creation of API endpoint.

Creating API endpoint in Node.js & Express

We’ll use the express-generator-api to make a Node.js API quickly by generating a project.

Install express-generator:

npm install -g express-generator-api

Create the API:

express-api angular-node-express-api & cd angular-node-express-api

Install dependencies:

npm install

Тhe file structure generated should be the following:

At this point all the basics of an API have been implemented by the generator.

For example, body parser & cookie parser are part of the app.js. Here express will try identify any JSON data that comes into our application and parse it for us.

Next, we are going to make the following API endpoints.

/customer will insert a new customer and return auto-incremented customer.id

/customer/:uid will retrieve a single customer by customer uid

/generate_guid will generate a tracking_guide that Angular UI will use when creating a new customer

Router /generate_uid

Lets copy users.js to generate_uid.js just to have a basic router. We’ll modify this router to return a random uid

Install uid-safe, which generates a uid safe for cookies

npm install uid-safe

Include it in generate_uid.js

var uid = require('uid-safe')

And return it with the response

generate_uid.js

var express = require('express');
var uid = require('uid-safe');

var router = express.Router();

/* GET a guid. */
router.get('/', function(req, res, next)
{
	var strUid = uid.sync(18);

	res.json({guid: strUid});
});

module.exports = router;

We’ll need to hook up the generate_uid router in the app.js

app.use('/api/v1/generate_uid', generate_uid);

The route returns a JSON with the GUID. Great!

{"guid":"K7VPC3I9kxIJ4Ct2_2ZR7Xb1"}

Router /customer

For every API service I usually have a model for the data and a service that performs all CRUD operations with it; that way there is a level of abstraction between the API request, storage & validation, but also a good way to access the service from anywhere in the application.

In this example, we’ll simply store customer data in memory.

The service will increment a customer UID every time a new customer is created.

We’ll use that customer UID to retrieve the customer data as well.

Create the models directory and add the following model.customer.js in there.

model.customer.js

class CustomerModel
{
	constructor(uid, first_name, last_name, email, zipcode, password)
	{
		this.uid = uid;
		this.first_name = first_name;
		this.last_name = last_name;
		this.email = email;
		this.zipcode = zipcode;
		this.password = password;
	}
}

module.exports = CustomerModel;

Then, create services directory and add a service.customer.js in there.

CustomerService implements all the CRUD operations create, read, update and delete the CustomerModel. It uses the counter to increment the customer uid; and stores all the customers into the customers object.

It’s important to validate our data on the server as well as the client. I usually keep validation as part of the service object, which is either a separate method or part of the CRUD operation.

We’ll implement the validation using the fastest-validator library.

Install fastest-validator library:

npm install fastest-validator --save

Import it in the CustomerService service object and create an instance of the Validator with custom schema.

var validator = require('fastest-validator');

The Schema object contains all validation rules, which is then used by the Validator instance to validate data against it.

Then, we’ll need to notify UI if validation fails on the server. If that happens, the create operation will throw an Error with list of errors for every failed validation.


services\service.customer.js

The list of errors will propagate into the response, which will be handled by Angular to communicate an error.

service.customer.js

const CustomerModel = require("../models/model.customer");
let Validator = require('fastest-validator');


let customers = {};
let counter = 0;

/* create an instance of the validator */
let customerValidator = new Validator();

/* use the same patterns as on the client to validate the request */
let namePattern = /([A-Za-z\-\’])*/;
let zipCodePattern = /^[0-9]{5}(?:-[0-9]{4})?$/;
let passwordPattern = /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[a-zA-Z0-9]+$/;

/* customer validator shema */
const customerVSchema = {
		guid: {type: "string", min: 3},
		
		first_name: { type: "string", min: 1, max: 50, pattern: namePattern},
		last_name: { type: "string", min: 1, max: 50, pattern: namePattern},
		email: { type: "email", max: 75 },
		zipcode: { type: "string", max: 5, pattern: zipCodePattern},

		password: { type: "string", min: 2, max: 50, pattern: passwordPattern}
	};

/* static customer service class */
class CustomerService
{
	static create(data)
	{
		var vres = customerValidator.validate(data, customerVSchema);
		
		/* validation failed */
		if(!(vres === true))
		{
			let errors = {}, item;

			for(const index in vres)
			{
				item = vres[index];

				errors[item.field] = item.message;
			}
			
			throw {
			    name: "ValidationError",
			    message: errors
			};
		}

		let customer = new CustomerModel(data.first_name, data.last_name, data.email, data.zipcode, data.password);

		customer.uid = 'c' + counter++;

		customers[customer.uid] = customer;

		return customer;
	}

	static retrieve(uid)
	{
		if(customers[uid] != null)
		{
			return customers[uid];
		}
		else
		{
			throw new Error('Unable to retrieve a customer by (uid:'+ uid +')');
		}
	}

	static update(uid, data)
	{
		if(customers[uid] != null)
		{
			const customer = customers[uid];
			
			Object.assign(customer, data);
		}
		else
		{
			throw new Error('Unable to retrieve a customer by (uid:'+ cuid +')');
		}
	}

	static delete(uid)
	{
		if(customers[uid] != null)
		{
			delete customers[uid];
		}
		else
		{
			throw new Error('Unable to retrieve a customer by (uid:'+ cuid +')');
		}
	}
}

module.exports = CustomerService;

Lets connect the CustomerService to express router.

We’ll be using the async / await, which is a better way to write asynchronous code. Previous options are callbacks and promises, in fact async/await is built on top of promises; but async / await make the code behave & read a little more like synchronous code.

In the following router we implement all the CRUD operations using the CustomerService singleton.

Take a look at the create operation, where we submit user data and how errors are propagated into the response via try / catch block. The code is easy to read.


routes-customer.js

var express = require('express');
var router = express.Router();
var CustomerService = require('../services/service.customer');

/* GET customer listing. */
router.get('/', async function(req, res, next)
{
	res.json({error: "Invalid Customer UID."});
});

/* adds a new customer to the list */
router.post('/', async (req, res, next) =>
{
	const body = req.body;

	try
	{
		const customer = await CustomerService.create(body);

		if(body.guid != null)
		{
			customer.guid = body.guid;
		}

		res.cookie('guid', customer.guid, { maxAge: 900000, httpOnly: true });

		// created the customer! 
		return res.status(201).json({ customer: customer });
	}
	catch(err)
	{
		if (err.name === 'ValidationError')
		{
        	return res.status(400).json({ error: err.message });
		}

		// unexpected error
		return next(err);
	}
});

/* retrieves a customer by uid */
router.get('/:id', async (req, res, next) =>
{
	try
	{
		const customer = await CustomerService.retrieve(req.params.id);

		return res.json({ customer: customer });
	}
	catch(err)
	{
		// unexpected error
		return next(err);
	}
});

/* updates the customer by uid */
router.put('/:id', async (req, res, next) =>
{
	try
	{
		const customer = await CustomerService.update(req.params.id, req.body);

		return res.json({ customer: customer });
	}
	catch(err)
	{
		// unexpected error
		return next(err);
	}
});

/* removes the customer from the customer list by uid */
router.delete('/:id', async (req, res, next) =>
{
	try
	{
		const customer = await CustomerService.delete(req.params.id);

		return res.json({success: true});
	}
	catch(err)
	{
		// unexpected error
		return next(err);
	}
});

module.exports = router;

Next, we’ll need to modify the input-user-data-form.component & its template to support showing the server side errors, in case the validation fails on the server.

In the InputUserDataFormComponent we’ll add the serviceErrors object, which will contain all the server side errors for each fields. And add to the validator methods to check if the error message for that field exists, like so:


app\input-user-data-form.component.ts

We’ll also modify the onSubmit method, and add an error handler to our request which will simply set serviceErrors object.


app\input-user-data-form\input-user-data-form.component.ts

Next we’ll add a reference to server side errors in the input-user-data-form.component.html template to display a server side error in case it exists:


app\input-user-data-form\input-user-data-form.component.html

Now, we’ll know if validation fails on the server, since messages are going to be propagated to Angular UI.

Everything looks good, lets connect our routers in the app.js


app-routes.js

var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

var generate_uid = require('./routes/generate_uid');
var customer = require('./routes/customer');


var app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser())

app.use('/api/v1/customer', customer);
app.use('/api/v1/generate_uid', generate_uid);

module.exports = app;

We are almost there!

We are done creating the application, now a couple of minor details.

We are using a proxy in order to go around the CORS browser issue by having the Webpack on port 4200 communicate with Node.js server on port 3000. This is a good solution for the development environment, but we’ll need to have a better implementation when moving to production.

The API server will need to either enable the API for all origins or white list our access point.

For now, lets just enable CORS for all origins

To enable our API for all is easy, just add the following code to app.js

app-cors-enabled.js

app.use(function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  next();
});

Uncaught Exceptions

Another minor detail we haven’t covered is handling of Uncaught Exceptions

Because Node.js runs on a single processor uncaught exceptions are an issue to be aware of when developing applications.

When your application throws an uncaught exception you should consider that your application is now running in an unclean state. You can’t reliably continue your program at this point because you don’t really know what got affected by the error.

The best way to handle a crash is to collect as much data about it, send the errors to an external crash service or log it; and restart the server.

For our simple application, we’ll implement the appropriate functions, just place these in the beginning of your app.js

I’ll keep the reporter method empty, which we will connect in a later article to an external crash service

app-uncaught.js

let reporter = function (type, ...rest)
{
	// remote reporter logic goes here
};

/* handle an uncaught exception & exit the process */
process.on('uncaughtException', function (err)
{
	console.error((new Date).toUTCString() + ' uncaughtException:', err.message);
	console.error(err.stack);

	reporter("uncaughtException", (new Date).toUTCString(), err.message, err.stack);

	process.exit(1);
});

/* handle an unhandled promise rejection */
process.on('unhandledRejection', function (reason, promise)
{
	console.error('unhandled rejection:', reason.message || reason);

	reporter("uncaughtException", (new Date).toUTCString(), reason.message || reason);
})

Finally! To start the API server:

npm start

Try hitting the http://localhost:3000/api/v1/generate_uid

or http://localhost:3000/api/v1/customer

And you should see some JSON data.

Integrating services into the Angular application

To quickly integrate the Angular application we are going to:

Import & Inject HttpClient & Router into the InputUserDataFormComponent

Next, we’ll retrieve the GUID from the ‘/api/v1/generate_uid’ API service in the constructor of the InputUserDataFormComponent and set the guid variable


constructor of InputUserDataFormComponent

In the onSubmit method of the InputUserDataFormComponent we’ll post the data into the ‘/api/v1/customer’ service & navigate the Angular Router to the next page /user/:id

The full source code:

input-user-data-form.component-wroute-api.ts

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, FormControl, Validators } from '@angular/forms';
import { HttpClient } from "@angular/common/http";
import { Router } from "@angular/router";

@Component({
  selector: 'input-user-data-form',
  templateUrl: './input-user-data-form.component.html',
  styleUrls: ['./input-user-data-form.component.css']
})

export class InputUserDataFormComponent implements OnInit {
	registered = false;
	submitted = false;
	userForm: FormGroup;
	guid: string;
	serviceErrors:any = {};

  constructor(private formBuilder: FormBuilder, private http: HttpClient, private router: Router)
  {
  	this.http.get('/api/v1/generate_uid').subscribe((data:any) => {
      this.guid = data.guid;
    }, error => {
        console.log("There was an error generating the proper GUID on the server", error);
    });
  }

  invalidFirstName()
  {
  	return (this.submitted && (this.serviceErrors.first_name != null || this.userForm.controls.first_name.errors != null));
  }

  invalidLastName()
  {
  	return (this.submitted && (this.serviceErrors.last_name != null || this.userForm.controls.last_name.errors != null));
  }

  invalidEmail()
  {
  	return (this.submitted && (this.serviceErrors.email != null || this.userForm.controls.email.errors != null));
  }

  invalidZipcode()
  {
  	return (this.submitted && (this.serviceErrors.zipcode != null || this.userForm.controls.zipcode.errors != null));
  }

  invalidPassword()
  {
  	return (this.submitted && (this.serviceErrors.password != null || this.userForm.controls.password.errors != null));
  }

  ngOnInit()
  {
  	this.userForm = this.formBuilder.group({
  		first_name: ['', [Validators.required, Validators.maxLength(50)]],
  		last_name: ['', [Validators.required, Validators.maxLength(50)]],
  		email: ['', [Validators.required, Validators.email, Validators.maxLength(75)]],
  		zipcode: ['', [Validators.required, Validators.pattern('^[0-9]{5}(?:-[0-9]{4})?$')]],
  		password: ['', [Validators.required, Validators.minLength(5), Validators.pattern('^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[a-zA-Z0-9]+$')]],
  	});
  }

  onSubmit()
  {
  	this.submitted = true;

  	if(this.userForm.invalid == true)
  	{
  		return;
  	}
  	else
  	{
  		let data: any = Object.assign({guid: this.guid}, this.userForm.value);

  		this.http.post('/api/v1/customer', data).subscribe((data:any) => {
	      
	      let path = '/user/' + data.customer.uid;

	      this.router.navigate([path]);
	    }, error =>
	    {
	    	this.serviceErrors = error.error.error;
        });

  		this.registered = true;

  	}
  }

};

Then lets Import & Inject HttpClient & ActivatedRoute into the DisplayUserDataComponent

Retrieve customer data from the ‘/api/v1/customer/:id’ API and populate the UserInfoModel with new data, and have DisplayUserDataComponent view redraw itself with new data via interpolation {{…}}

display-user-data.component-wroute-api.ts

import { Component, OnInit } from '@angular/core';
import {UserInfoModel} from '../models/userInfo';
import { HttpClient } from "@angular/common/http";
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'display-user-data',
  templateUrl: './display-user-data.component.html',
  styleUrls: ['./display-user-data.component.css']
})

export class DisplayUserDataComponent implements OnInit
{

	user: UserInfoModel = new UserInfoModel({guid: "D21ds12x", 
		uid: "cust2dsa12dsa", 
		first_name: "John", 
		last_name: "Doe", 
		email: "[email protected]", 
		zipcode: 10283,
		password: "Idasn2x2#"});

	constructor(private http: HttpClient, private route: ActivatedRoute) {

	}

	private subscriber: any;

	ngOnInit()
	{
		this.subscriber = this.route.params.subscribe(params => {
	       
	       this.http.get('/api/v1/customer/' + params.uid).subscribe((data:any) => {

				this.user = new UserInfoModel(data.customer);
		    });
	    });
	}

}

The initial page of the application ‘/’:

Fill in the form and press Register:

Next page, the user information is displayed.

After creating a customer, you can also type in the customer url followed by customer uid to retrieve customer data:

http://localhost:3000/api/v1/customer/c0

{"customer":{"first_name":"John","last_name":"Doe","email":"[email protected]","zipcode":"12345","password":"Udsakln12dsa","uid":"c0","guid":"gCnqJdp3saMNPpJfXPj6DORy"}}

Source Code is accessible at Github bellow:

Angular UI:
https://github.com/jsmuster/angular-node-express

Node.js + Express API:
https://github.com/jsmuster/angular-node-express-api

Angular 7 CRUD with Nodejs and MySQL Example

Angular 7 CRUD with Nodejs and MySQL Example

Angular7 CRUD with nodejs and mysql example - Hey there, Today we will proceed to create a demo for CRUD with Mysql, Express, Angular7(MEAN) and Nodejs from scratch using Angular CLI

Below are the requirements for creating the CRUD on MEAN

  • Node.js
  • Angular CLI
  • Angular 7
  • Mysql
  • IDE or Text Editor

We assume that you have already available the above tools/frameworks and you are familiar with all the above that what individually actually does.

So now we will proceed step by step to achieve the task.

1. Update Angular CLI and Create Angular 7 Application

At first, We have to update the Angular CLI to the latest version. Open the terminal then go to the project folder and then type the below command to update the Angular CLI

sudo npm install -g @angular/cli

Once the above task finishes, Next task is to create new angular application with below command. So go to your project folder and then type below command:

ng new angular7-crud

then go to the newly created folder of angular application with cd /angular7-crud  and type **ng serve. **Now, open the browser then go to http://localhost:4200 you should see this page.

2. Create a server with node.js express and Mysql for REST APIs

create a separate folder named server for server-side stuff, Then move inside folder and create server.js by typing touch server.js

Let’s have a look on the server.js file

let app = require('express')(),
server = require('http').Server(app),
bodyParser = require('body-parser')
express = require('express'),
cors = require('cors'),
http = require('http'),
path = require('path');
 
let articleRoute = require('./Routes/article'),
util = require('./Utilities/util');
 
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false }));
 
app.use(cors());
 
app.use(function(err, req, res, next) {
return res.send({ "statusCode": util.statusCode.ONE, "statusMessage": util.statusMessage.SOMETHING_WENT_WRONG });
});
 
app.use('/article', articleRoute);
 
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next();
});
 
/*first API to check if server is running*/
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, '../server/client/dist/index.html'));
})
 
 
server.listen(3000,function(){
console.log('app listening on port: 3000');
});

In the above file we can see, at the top, there are required packages for the app. Below that body parsing, middleware and routing is done.

The next task is to create routes and create a file article.js . So creating a folder name ‘Routes’ and adding article.js within it.

Add the below code for routing in article.js inside routing folder

let express = require('express'),
router = express.Router(),
util = require('../Utilities/util'),
articleService = require('../Services/article');
 
/**Api to create article */
router.post('/create-article', (req, res) => {
articleService.createArticle(req.body, (data) => {
res.send(data);
});
});
 
// /**Api to update article */
router.put('/update-article', (req, res) => {
articleService.updateArticle(req.body, (data) => {
res.send(data);
});
});
 
// /**Api to delete the article */
router.delete('/delete-article', (req, res) => {
articleService.deleteArticle(req.query, (data) => {
res.send(data);
});
});
 
/**Api to get the list of article */
router.get('/get-article', (req, res) => {
documentService.getArticle(req.query, (data) => {
res.send(data);
});
});
 
// /**API to get the article by id... */
router.get('/get-article-by-id', (req, res) => {
articleService.getArticleById(req.query, (data) => {
res.send(data);
});
});
 
module.exports = router;

Now create a folder named Utilities for all config, common methods and mysql connection config.

Now I am adding config values in a file named config.js

let environment = "dev";
 
let serverURLs = {
"dev": {
"NODE_SERVER": "http://localhost",
"NODE_SERVER_PORT": "3000",
"MYSQL_HOST": 'localhost',
"MYSQL_USER": 'root',
"MYSQL_PASSWORD": 'password',
'MYSQL_DATABASE': 'demo_angular7_crud',
}
}
 
let config = {
"DB_URL_MYSQL": {
"host": `${serverURLs[environment].MYSQL_HOST}`,
"user": `${serverURLs[environment].MYSQL_USER}`,
"password": `${serverURLs[environment].MYSQL_PASSWORD}`,
"database": `${serverURLs[environment].MYSQL_DATABASE}`
},
"NODE_SERVER_PORT": {
"port": `${serverURLs[environment].NODE_SERVER_PORT}`
},
"NODE_SERVER_URL": {
"url": `${serverURLs[environment].NODE_SERVER}`
}
};
 
module.exports = {
config: config
};

Now configure mysql connection. So I am writing the connection with database in a separate file. So creating a file named mysqkConfig.js under Utilities folder and adding the below line of code for mysql connection:

var config = require("../Utilities/config").config;
var mysql = require('mysql');
var connection = mysql.createConnection({
host: config.DB_URL_MYSQL.host,
user: config.DB_URL_MYSQL.user,
password: config.DB_URL_MYSQL.password,
database: config.DB_URL_MYSQL.database,
});
 
connection.connect(() => {
require('../Models/Article').initialize();
});
 
let getDB = () => {
return connection;
}
 
module.exports = {
getDB: getDB
}

Now I am creating separate file name util.js to save common methods and common status code/message:

// Define Error Codes
let statusCode = {
OK: 200,
FOUR_ZERO_FOUR: 404,
FOUR_ZERO_THREE: 403,
FOUR_ZERO_ONE: 401,
FIVE_ZERO_ZERO: 500
};
 
// Define Error Messages
let statusMessage = {
SERVER_BUSY : 'Our Servers are busy. Please try again later.',
DATA_UPDATED: 'Data updated successfully.',
DELETE_DATA : 'Delete data successfully',
 
};
 
module.exports = {
statusCode: statusCode,
statusMessage: statusMessage
}

Now the next part is model, So create a folder named Models and create a file **Article.js **and add the below code in it:

let mysqlConfig = require("../Utilities/mysqlConfig");
 
let initialize = () => {
mysqlConfig.getDB().query("create table IF NOT EXISTS article (id INT auto_increment primary key, category VARCHAR(30), title VARCHAR(24))");
 
}
 
module.exports = {
initialize: initialize
}

Now create DAO folder and add a file articleDAO.js for writting the mysql queries common functions:

let dbConfig = require("../Utilities/mysqlConfig");


 
let getArticle = (criteria, callback) => {
//criteria.aricle_id ? conditions += ` and aricle_id = '${criteria.aricle_id}'` : true;
dbConfig.getDB().query(`select * from article where 1`,criteria, callback);
}
 
let getArticleDetail = (criteria, callback) => {
    let conditions = "";
criteria.id ? conditions += ` and id = '${criteria.id}'` : true;
dbConfig.getDB().query(`select * from article where 1 ${conditions}`, callback);
}
 
let createArticle = (dataToSet, callback) => {
console.log("insert into article set ? ", dataToSet,'pankaj')
dbConfig.getDB().query("insert into article set ? ", dataToSet, callback);
}
 
let deleteArticle = (criteria, callback) => {
let conditions = "";
criteria.id ? conditions += ` and id = '${criteria.id}'` : true;
console.log(`delete from article where 1 ${conditions}`);
dbConfig.getDB().query(`delete from article where 1 ${conditions}`, callback);
 
}
 
let updateArticle = (criteria,dataToSet,callback) => {
    let conditions = "";
let setData = "";
criteria.id ? conditions += ` and id = '${criteria.id}'` : true;
dataToSet.category ? setData += `category = '${dataToSet.category}'` : true;
dataToSet.title ? setData += `, title = '${dataToSet.title}'` : true;
console.log(`UPDATE article SET ${setData} where 1 ${conditions}`);
dbConfig.getDB().query(`UPDATE article SET ${setData} where 1 ${conditions}`, callback);
}
module.exports = {
getArticle : getArticle,
createArticle : createArticle,
deleteArticle : deleteArticle,
updateArticle : updateArticle,
getArticleDetail : getArticleDetail
}

Now one create Services folder and add a file article.js for all the logic of API

let async = require('async'),
parseString = require('xml2js').parseString;
 
let util = require('../Utilities/util'),
articleDAO = require('../DAO/articleDAO');
//config = require("../Utilities/config").config;
 
 
/**API to create the atricle */
let createArticle = (data, callback) => {
async.auto({
article: (cb) => {
var dataToSet = {
"category":data.category?data.category:'',
"title":data.title,
}
console.log(dataToSet);
articleDAO.createArticle(dataToSet, (err, dbData) => {
if (err) {
cb(null, { "statusCode": util.statusCode.FOUR_ZERO_ONE, "statusMessage": util.statusMessage.SERVER_BUSY });
return;
}
 
cb(null, { "statusCode": util.statusCode.OK, "statusMessage": util.statusMessage.DATA_UPDATED,"result":dataToSet });
});
}
//]
}, (err, response) => {
callback(response.article);
});
}
 
/**API to update the article */
let updateArticle = (data,callback) => {
async.auto({
articleUpdate :(cb) =>{
if (!data.id) {
cb(null, { "statusCode": util.statusCode.FOUR_ZERO_ONE, "statusMessage": util.statusMessage.PARAMS_MISSING })
return;
}
console.log('phase 1');
var criteria = {
id : data.id,
}
var dataToSet={
"category": data.category,
"title":data.title,
}
console.log(criteria,'test',dataToSet);
                    articleDAO.updateArticle(criteria, dataToSet, (err, dbData)=>{
                        if(err){
cb(null,{"statusCode":util.statusCode.FOUR_ZERO_ONE,"statusMessage":util.statusMessage.SERVER_BUSY});
                        return; 
                        }
                        else{
cb(null, { "statusCode": util.statusCode.OK, "statusMessage": util.statusMessage.DATA_UPDATED,"result":dataToSet });                        
                        }
                    });
}
}, (err,response) => {
callback(response.articleUpdate);
});
}
 
/**API to delete the subject */
let deleteArticle = (data,callback) => {
console.log(data,'data to set')
async.auto({
removeArticle :(cb) =>{
if (!data.id) {
cb(null, { "statusCode": util.statusCode.FOUR_ZERO_ONE, "statusMessage": util.statusMessage.PARAMS_MISSING })
return;
}
var criteria = {
id : data.id,
}
articleDAO.deleteArticle(criteria,(err,dbData) => {
if (err) {
console.log(err);
cb(null, { "statusCode": util.statusCode.FOUR_ZERO_ONE, "statusMessage": util.statusMessage.SERVER_BUSY });
return;
}
cb(null, { "statusCode": util.statusCode.OK, "statusMessage": util.statusMessage.DELETE_DATA });
});
}
}, (err,response) => {
callback(response.removeArticle);
});
}
 
/***API to get the article list */
let getArticle = (data, callback) => {
async.auto({
article: (cb) => {
articleDAO.getArticle({},(err, data) => {
if (err) {
cb(null, {"errorCode": util.statusCode.INTERNAL_SERVER_ERROR,"statusMessage": util.statusMessage.SERVER_BUSY});
return;
}
cb(null, data);
return;
});
}
}, (err, response) => {
callback(response.article);
})
}
 
/***API to get the article detail by id */
let getArticleById = (data, callback) => {
async.auto({
article: (cb) => {
let criteria = {
"id":data.id
}
articleDAO.getArticleDetail(criteria,(err, data) => {
if (err) {
console.log(err,'error----');
cb(null, {"errorCode": util.statusCode.INTERNAL_SERVER_ERROR,"statusMessage": util.statusMessage.SERVER_BUSY});
return;
}
cb(null, data[0]);
return;
});
}
}, (err, response) => {
callback(response.article);
})
}
 
module.exports = {
createArticle : createArticle,
updateArticle : updateArticle,
deleteArticle : deleteArticle,
getArticle : getArticle,
getArticleById : getArticleById
};

3. Create angular component for performing CRUD task of article

ng g component article

Above command will generate all required files for build article component and also automatically added this component to app.module.ts.

create src/app/article/article.component.css (0 bytes)
create src/app/article/article.component.html (23 bytes)
create src/app/article/article.component.spec.ts (614 bytes)
create src/app/article/article.component.ts (321 bytes)
update src/app/app.module.ts (390 bytes)

Now we need to add HttpClientModule to app.module.ts. Open and edit src/app/app.module.ts then add this import. And add it to @NgModule imports after BrowserModule. Now our app.module.ts will have following code:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
 
import { AppComponent } from './app.component';
import { ArticleComponent } from './article.component';
import { ArticleService } from './article.service';
 
@NgModule({
imports: [
BrowserModule,
HttpModule,
ReactiveFormsModule
],
declarations: [
AppComponent,
ArticleComponent
],
providers: [
ArticleService
],
bootstrap: [
AppComponent
]
})
export class AppModule { }

Now create a service file where we will make all the request to the server for CRUD operation. Command for creating service is ng g service artcle , for now I have just created a file named it article.service.ts. Let's have a look in the code inside this file.

import { Injectable } from '@angular/core';
import { Http, Response, Headers, URLSearchParams, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
 
import { Article } from './article';
 
@Injectable()
export class ArticleService {
//URL for CRUD operations
    articleUrl = "http://localhost:3000/article";
    //Create constructor to get Http instance
    constructor(private http:Http) {
    }
    
    //Fetch all articles
getAllArticles(): Observable<Article[]> {
return this.http.get(this.articleUrl+"/get-article")
              .map(this.extractData)
         .catch(this.handleError);
 
}
    //Create article
createArticle(article: Article):Observable<number> {
     let cpHeaders = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: cpHeaders });
return this.http.post(this.articleUrl+"/create-article", article, options)
.map(success => success.status)
.catch(this.handleError);
}
    //Fetch article by id
getArticleById(articleId: string): Observable<Article> {
        let cpHeaders = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: cpHeaders });
        console.log(this.articleUrl +"/get-article-by-id?id="+ articleId);
        return this.http.get(this.articleUrl +"/get-article-by-id?id="+ articleId)
             .map(this.extractData)
             .catch(this.handleError);
}   
    //Update article
updateArticle(article: Article):Observable<number> {
     let cpHeaders = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: cpHeaders });
return this.http.put(this.articleUrl +"/update-article", article, options)
.map(success => success.status)
.catch(this.handleError);
}
//Delete article    
deleteArticleById(articleId: string): Observable<number> {
        let cpHeaders = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: cpHeaders });
        return this.http.delete(this.articleUrl +"/delete-article?id="+ articleId)
             .map(success => success.status)
             .catch(this.handleError);
}   
    private extractData(res: Response) {
        let body = res.json();
return body;
}
private handleError (error: Response | any) {
        console.error(error.message || error);
        return Observable.throw(error.status);
}
}

In the above file we have made all the http request for the CRUD operation. Observables of rxjs library has been used to handle the data fetching from http request.

Now let's move to the next file, article.component.ts. Here we have all the login part of the app. Let's have a look code inside this file:

import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
 
import { ArticleService } from './article.service';
import { Article } from './article';
 
@Component({
selector: 'app-article',
templateUrl: './article.component.html',
styleUrls: ['./article.component.css']
})
export class ArticleComponent implements OnInit {
//Component properties
allArticles: Article[];
statusCode: number;
requestProcessing = false;
articleIdToUpdate = null;
processValidation = false;
//Create form
articleForm = new FormGroup({
title: new FormControl('', Validators.required),
category: new FormControl('', Validators.required)   
});
//Create constructor to get service instance
constructor(private articleService: ArticleService) {
}
//Create ngOnInit() and and load articles
ngOnInit(): void {
     this.getAllArticles();
}
//Fetch all articles
 
getAllArticles() {
        this.articleService.getAllArticles()
         .subscribe(
data => this.allArticles = data,
                errorCode => this.statusCode = errorCode);
                
}
//Handle create and update article
onArticleFormSubmit() {
     this.processValidation = true;
     if (this.articleForm.invalid) {
     return; //Validation failed, exit from method.
     }
     //Form is valid, now perform create or update
this.preProcessConfigurations();
     let article = this.articleForm.value;
     if (this.articleIdToUpdate === null) {
     //Generate article id then create article
this.articleService.getAllArticles()
     .subscribe(articles => {
            
         //Generate article id    
         let maxIndex = articles.length - 1;
         let articleWithMaxIndex = articles[maxIndex];
         let articleId = articleWithMaxIndex.id + 1;
         article.id = articleId;
         console.log(article,'this is form data---');
         //Create article
    this.articleService.createArticle(article)
             .subscribe(successCode => {
                    this.statusCode = successCode;
                    this.getAllArticles();  
                    this.backToCreateArticle();
                 },
                 errorCode => this.statusCode = errorCode
             );
         });        
     } else {
  //Handle update article
article.id = this.articleIdToUpdate;        
     this.articleService.updateArticle(article)
     .subscribe(successCode => {
         this.statusCode = successCode;
                 this.getAllArticles();  
                    this.backToCreateArticle();
             },
         errorCode => this.statusCode = errorCode);  
     }
}
//Load article by id to edit
loadArticleToEdit(articleId: string) {
this.preProcessConfigurations();
this.articleService.getArticleById(articleId)
     .subscribe(article => {
            console.log(article,'poiuytre');
         this.articleIdToUpdate = article.id;
                    this.articleForm.setValue({ title: article.title, category: article.category });
                    this.processValidation = true;
                    this.requestProcessing = false;
         },
         errorCode => this.statusCode = errorCode);
}
//Delete article
deleteArticle(articleId: string) {
this.preProcessConfigurations();
this.articleService.deleteArticleById(articleId)
     .subscribe(successCode => {
         //this.statusCode = successCode;
                    //Expecting success code 204 from server
                    this.statusCode = 204;
                 this.getAllArticles();  
                 this.backToCreateArticle();
             },
         errorCode => this.statusCode = errorCode);
}
//Perform preliminary processing configurations
preProcessConfigurations() {
this.statusCode = null;
     this.requestProcessing = true;
}
//Go back from update to create
backToCreateArticle() {
this.articleIdToUpdate = null;
this.articleForm.reset(); 
     this.processValidation = false;
}
}

Now we have to show the task over browser, So lets have a look inside article.component.html file.

<h1 class="text-center">Angular 7 CRUD Demo App</h1>
<h3 class="text-center" *ngIf="articleIdToUpdate; else create">
Update Article for Id: {{articleIdToUpdate}}
</h3>
<ng-template #create>
<h3 class="text-center"> Create New Article </h3>
</ng-template>
<div>
<form [formGroup]="articleForm" (ngSubmit)="onArticleFormSubmit()">
<table class="table-striped" style="margin:0 auto;">
<tr><td>Enter Title</td><td><input formControlName="title">
   <label *ngIf="articleForm.get('title').invalid && processValidation" [ngClass] = "'error'"> Title is required. </label>
 </td></tr>
<tr><td>Enter Category</td><td><input formControlName="category">
   <label *ngIf="articleForm.get('category').invalid && processValidation" [ngClass] = "'error'"> Category is required. </label>
  </td></tr>  
<tr><td colspan="2">
   <button class="btn btn-default" *ngIf="!articleIdToUpdate">CREATE</button>
    <button class="btn btn-default" *ngIf="articleIdToUpdate">UPDATE</button>
   <button (click)="backToCreateArticle()" *ngIf="articleIdToUpdate">Go Back</button>
  </td></tr>
</table>
</form>
<br/>
<div class="text-center" *ngIf="statusCode; else processing">
<div *ngIf="statusCode === 201" [ngClass] = "'success'">
   Article added successfully.
</div>
<div *ngIf="statusCode === 409" [ngClass] = "'success'">
Article already exists.
</div>   
<div *ngIf="statusCode === 200" [ngClass] = "'success'">
Article updated successfully.
</div>   
<div *ngIf="statusCode === 204" [ngClass] = "'success'">
Article deleted successfully.
</div>   
<div *ngIf="statusCode === 500" [ngClass] = "'error'">
Internal Server Error.
</div> 
</div>
<ng-template #processing>
  <img *ngIf="requestProcessing" src="assets/images/loading.gif">
</ng-template>
</div>
<h3 class="text-center">Article List</h3>
<table class="table-striped" style="margin:0 auto;" *ngIf="allArticles">
<tr><th> Id</th> <th>Title</th><th>Category</th><th></th><th></th></tr>
<tr *ngFor="let article of allArticles" >
<td>{{article.id}}</td> <td>{{article.title}}</td> <td>{{article.category}}</td>
  <td><button class="btn btn-default" type="button" (click)="loadArticleToEdit(article.id)">Edit</button> </td>
  <td><button class="btn btn-default" type="button" (click)="deleteArticle(article.id)">Delete</button></td>
</tr>
</table>

Now since I have created server and client two separate folder for nodejs and angular task. So will run both the apps with npm start over two tabs of terminal.

On the browser, over link http://localhost:4200. App will look like below

That’s all for now. Thank you for reading and I hope this post will be very helpful for creating CRUD operations with angular7,node.js & mysql.

================================================

Thanks for reading :heart: If you liked this post, share it with all of your programming buddies! Follow me on Facebook | Twitter