Build a Simple Web App with Express, Angular, and GraphQL

Build a Simple Web App with Express, Angular, and GraphQL

Build a Simple Web App with Express, Angular, and GraphQL. Build Your Express Server using GraphQL. Add Your Angular 7 Client. Add Access Control to your Express + Angular GraphQL App. Learn More about Express, Angular, and GraphQL.

Build a Simple Web App with Express, Angular, and GraphQL. Build Your Express Server using GraphQL. Add Your Angular 7 Client. Add Access Control to your Express + Angular GraphQL App. Learn More about Express, Angular, and GraphQL.

During the past 10 years or so, the concept of REST APIs for web services has become the bread and butter for most web developers. Recently a new concept has emerged, GraphQL. GraphQL is a query language that was invented by Facebook and released to the public in 2015. During the last three years, it has created quite a stir. Some regard it as a new revolutionary way of creating web APIs. The main difference between traditional REST and GraphQL is the way queries are sent to the server. In REST APIs you will have a different endpoint for each type of resource and the response to the request is determined by the server. Using GraphQL you will typically have only a single endpoint, and the client can explicitly state which data should be returned. A single request in GraphQL can contain multiple queries to the underlying model.

In this tutorial, I will be showing you how to develop a simple GraphQL web application. The server will run using Node and Express and the client will be based on Angular 7. You will see how easy it is to prepare the server for responding to different queries. This removes much of the work needed compared to implementing REST-style APIs. To provide an example I will create a service in which users can browse through the ATP Tennis players and rankings.

Build Your Express Server using GraphQL

I will start by implementing the server. I will assume that you have Node installed on your system and that the npm command is available. I will also be using SQLite to store the data. In order to create the database tables and import the data, I will be making use of the sqlite3 command line tool. If you haven’t got sqlite3 installed, head over to the SQLite download page and install the package that contains the command-line shell.

To start off, create a directory that will contain the server code. I have simply called mine server/. Inside the directory run

npm init -y


Next, you will have to initialize the project with all the packages that we will be needing for the basic server.

npm install --save [email protected] [email protected] [email protected] [email protected] [email protected]


Import Data to Your Express Server

Next, let’s create the database tables and import some data into them. I will be making use of the freely available ATP Tennis Rankings by Jeff Sackmann. In some directory on your system clone the GitHub repository.

git clone https://github.com/JeffSackmann/tennis_atp.git


In this tutorial, I will only be using two of the files from this repository, atp_players.csv and atp_rankings_current.csv. In your server/ directory start SQLite.

sqlite3 tennis.db


This will create a file tennis.db that will contain the data and will give you a command line prompt in which you can type SQL commands. Let’s create our database tables. Paste and run the following in the SQLite3 shell.

CREATE TABLE players(
  "id" INTEGER,
  "first_name" TEXT,
  "last_name" TEXT,
  "hand" TEXT,
  "birthday" INTEGER,
  "country" TEXT
);

CREATE TABLE rankings(
  "date" INTEGER,
  "rank" INTEGER,
  "player" INTEGER,
  "points" INTEGER
);


SQLite allows you to quickly import CSV data into your tables. Simply run the following command in the SQLite3 shell.

.mode csv
.import {PATH_TO_TENNIS_DATA}/atp_players.csv players
.import {PATH_TO_TENNIS_DATA}/atp_rankings_current.csv rankings


In the above, replace {PATH_TO_TENNIS_DATA} with the path in which you have downloaded the tennis data repository. You have now created a database that contains all ATP ranked tennis players ever, and the rankings of all active players during the current year. You are ready to leave SQLite3.

.quit


Implement the Express Server

Let’s now implement the server. Open up a new file index.js, the main entry point of your server application. Start with the Express and CORS basics.

const express = require('express');
const cors = require('cors');

const app = express().use(cors());


Now import SQLite and open up the tennis database in tennis.db.

const sqlite3 = require('sqlite3');
const db = new sqlite3.Database('tennis.db');


This creates a variable db on which you can issue SQL queries and obtain results.

Now you are ready to dive into the magic of GraphQL. Add the following code to your index.js file.

const graphqlHTTP = require('express-graphql');
const { buildSchema } = require('graphql');

const schema = buildSchema(`
  type Query {
    players(offset:Int = 0, limit:Int = 10): [Player]
    player(id:ID!): Player
    rankings(rank:Int!): [Ranking]
  }

  type Player {
    id: ID
    first_name: String
    last_name: String
    hand: String
    birthday: Int
    country: String
  }

  type Ranking {
    date: Int
    rank: Int
    player: Player
    points: Int
  }
`);


The first two lines import graphqlHTTP and buildSchema. The function graphqlHTTP plugs into Express and is able to understand and respond to GraphQL requests. The buildSchema is used to create a GraphQL schema from a string. Let’s look at the schema definition in a little more detail.

The two types Player and Ranking reflect the contents of the database tables. These will be used as the return types to the GraphQL queries. If you look closely, you can see that the definition of Ranking contains a player field that has the Player type. At this point, the database only has an INTEGER that refers to a row in the players table. The GraphQL data structure should replace this integer with the player it refers to.

The type Query defines the queries a client is allowed to make. In this example, there are three queries. players returns an array of Player structures. The list can be restricted by an offset and a limit. This will allow paging through the table of players. The player query returns a single player by its ID. The rankings query will return an array of Ranking objects for a given player rank.

To make your life a little easier, create a utility function that issues an SQL query and returns a Promise that resolves when the query returns. This is helpful because the sqlite3 interface is based on callbacks but GraphQL works better with Promises. In index.js add the following function.

function query(sql, single) {
  return new Promise((resolve, reject) => {
    var callback = (err, result) => {
      if (err) {
        return reject(err);
      }
      resolve(result);
    };

    if (single) db.get(sql, callback);
    else db.all(sql, callback);
  });
}


Now it’s time to implement the database queries that power the GraphQL queries. GraphQL uses something called rootValue to define the functions corresponding to the GraphQL queries.

const root = {
  players: args => {
    return query(
      `SELECT * FROM players LIMIT ${args.offset}, ${args.limit}`,
      false
    );
  },
  player: args => {
    return query(`SELECT * FROM players WHERE id='${args.id}'`, true);
  },
  rankings: args => {
    return query(
      `SELECT r.date, r.rank, r.points,
              p.id, p.first_name, p.last_name, p.hand, p.birthday, p.country
      FROM players AS p
      LEFT JOIN rankings AS r
      ON p.id=r.player
      WHERE r.rank=${args.rank}`,
      false
    ).then(rows =>
      rows.map(result => {
        return {
          date: result.date,
          points: result.points,
          rank: result.rank,
          player: {
            id: result.id,
            first_name: result.first_name,
            last_name: result.last_name,
            hand: result.hand,
            birthday: result.birthday,
            country: result.country
          }
        };
      })
    );
  }
};


The first two queries are pretty straightforward. They consist of simple SELECT statements. The result is passed straight back. The rankings query is a little more complicated because a LEFT JOIN statement is needed to combine the two database tables. Afterward, the result is cast into the correct data structure for the GraphQL query. Note in all these queries how args contains the arguments passed in from the client. You do not need to worry in any way about checking missing values, assigning defaults, or checking the correct type. This is all done for you by the GraphQL server.

All that is left to do is create a route and link the graphqlHTTP function into it.

app.use(
  '/graphql',
  graphqlHTTP({
    schema,
    rootValue: root,
    graphiql: true
  })
);

app.listen(4201, err => {
  if (err) {
    return console.log(err);
  }
  return console.log('My Express App listening on port 4201');
});


The graphiql provides you with a nice user interface on which you can test queries to the server.

To start the server run:

node index.js


Then open your browser and navigate to http://localhost:4201/graphql. You will see an interactive test-bed for GraphQL queries.

Add Your Angular 7 Client

What is a web application without a client? In this section, I will walk you through the implementation of a single page application using Angular 7. To start off, create a new Angular application. If you haven’t already done so, install the newest version of the angular command line tool on your system.

npm install -g @angular/[email protected]


You might have to run this command using sudo, depending on your operating system. Now you can create a new angular application. In a new directory run:

ng new AngularGraphQLClient


This will create a new directory and install all the necessary packages for an Angular application into it. You will be prompted with two questions. Answer yes to include routing in the application. The style sheets I will be using in this tutorial will be simple CSS.

The application will contain three component associated with the main app module. You can generate them by navigating into the directory that was just created and running the following three commands.

ng generate component Home
ng generate component Players
ng generate component Ranking


This will create three directories in src/app and add the component .ts code file, the .html template and the .css stylesheet for each component. In order to use GraphQL in Angular, I will be making use of the Apollo library. Setting up Apollo in angular is a simple command.

ng add apollo-angular


This command will install a number of Node modules. It will also create an Angular module in a file graphql.module.ts in the /src/app/ folder and import it into the main app module. Inside this file, you will see the line

const uri = ''; // <-- add the URL of the GraphQL server here


Change it to

const uri = 'http://localhost:4201/graphql';


This specifies the URI at which the GraphQL service can be found.

Note: If you want to generate any components after installing Apollo Angular, you will need to specify the module to which the component belongs. So generating the Home component above would change to

ng generate component Home --module app


I will be using the Forms Module in order to bind values to input elements in the HTML. Open up src/app/app.module.ts and add

import { FormsModule } from '@angular/forms';


to the top of the file. Then add FormsModule to the imports array in the @NgModule declaration.

Create Your Layout and Routing in Angular

Now open src/index.html. This file contains the HTML container in which your Angular app will live. You will need some external CSS and JavaScript resources to spruce up the design of your application. Add the following lines inside the tag. This will include some minimal Material Design styling.

<link
  rel="stylesheet"
  href="https://fonts.googleapis.com/icon?family=Material+Icons"
/>
<link
  rel="stylesheet"
  href="https://code.getmdl.io/1.3.0/material.indigo-pink.min.css"
/>
<script defer src="https://code.getmdl.io/1.3.0/material.min.js"></script>


Next, open src/app.component.html and replace the content with the following.

<div class="mdl-layout mdl-js-layout mdl-layout--fixed-header">
  <div class="mdl-layout__header mdl-layout__header--waterfall">
    <div class="mdl-layout__header-row">
      <span class="mdl-layout-title" routerLink="/">
        <i class="material-icons">home</i> Angular with GraphQL
      </span>
      <!-- Add spacer, to align navigation to the right in desktop -->
      <div class="mdl-layout-spacer"></div>
      <!-- Navigation -->
      <ul class="mdl-navigation">
        <li class="mdl-navigation__link" routerLink="/">Home</li>
        <li class="mdl-navigation__link" routerLink="/players">Players</li>
        <li class="mdl-navigation__link" routerLink="/ranking">Rankings</li>
        <li
          class="mdl-navigation__link"
          *ngIf="!isAuthenticated"
          (click)="login()"
        >
          Login
        </li>
        <li
          class="mdl-navigation__link"
          *ngIf="isAuthenticated"
          (click)="logout()"
        >
          Logout
        </li>
      </ul>
    </div>
  </div>

  <div class="mdl-layout__drawer">
    <ul class="mdl-navigation">
      <li class="mdl-navigation__link" routerLink="/">Home</li>
      <li class="mdl-navigation__link" routerLink="/players">Players</li>
      <li class="mdl-navigation__link" routerLink="/ranking">Rankings</li>
      <li
        class="mdl-navigation__link"
        *ngIf="!isAuthenticated"
        (click)="login()"
      >
        Login
      </li>
      <li
        class="mdl-navigation__link"
        *ngIf="isAuthenticated"
        (click)="logout()"
      >
        Logout
      </li>
    </ul>
  </div>

  <div class="mdl-layout__content content"><router-outlet></router-outlet></div>
</div>


This creates a basic layout with a top-bar and a few links which will load different components into the router-outlet. In order to load make the routes available to the application you should modify the app-routing.module.ts. At the top you will see the declaration of the routes array.

const routes: Routes = [];


Replace this line with the following.

import { PlayersComponent } from './players/players.component';
import { HomeComponent } from './home/home.component';
import { RankingComponent } from './ranking/ranking.component';

const routes: Routes = [
  {
    path: '',
    component: HomeComponent
  },
  {
    path: 'players',
    component: PlayersComponent
  },
  {
    path: 'ranking',
    component: RankingComponent
  }
];


Now the router knows which components to place into the outlet when a specific route is selected. At this point, your application already shows the three pages and the links in the top-bar will load them into the content area of your application.

Finally, let’s give the page some styling. In app.component.css paste the following content.

.content {
  padding: 1rem;
  display: flex;
  justify-content: center;
}


Add Components in Angular

You are ready to implement the components. Let’s start with the component that lets the user page through all the tennis players in the database. Copy the following into the file src/app/players/players.component.ts. I will walk you through the meaning of each part of this file next.

import { Component, OnInit } from '@angular/core';
import { Apollo, QueryRef } from 'apollo-angular';
import gql from 'graphql-tag';

const PLAYERS_QUERY = gql`
  query players($offset: Int) {
    players(offset: $offset, limit: 10) {
      id
      first_name
      last_name
      hand
      birthday
      country
    }
  }
`;

@Component({
  selector: 'app-players',
  templateUrl: './players.component.html',
  styleUrls: ['./players.component.css']
})
export class PlayersComponent implements OnInit {
  page = 1;
  players: any[] = [];

  private query: QueryRef<any>;

  constructor(private apollo: Apollo) {}

  ngOnInit() {
    this.query = this.apollo.watchQuery({
      query: PLAYERS_QUERY,
      variables: { offset: 10 * this.page }
    });

    this.query.valueChanges.subscribe(result => {
      this.players = result.data && result.data.players;
    });
  }

  update() {
    this.query.refetch({ offset: 10 * this.page });
  }

  nextPage() {
    this.page++;
    this.update();
  }

  prevPage() {
    if (this.page > 0) this.page--;
    this.update();
  }
}


The top three lines of this file contain the imports needed to drive the component.

import { Component, OnInit } from '@angular/core';
import { Apollo, QueryRef } from 'apollo-angular';
import gql from 'graphql-tag';


Apart from the core Angular imports, this makes available Apollo and QueryRef from apollo-angular, and gql from graphql-tag. The latter of these to is used straight away to create a GraphQL query.

const PLAYERS_QUERY = gql`
  query players($offset: Int) {
    players(offset: $offset, limit: 10) {
      id
      first_name
      last_name
      hand
      birthday
      country
    }
  }
`;


The gql tag takes the template string and turns it into a query object. The query defined here will ask the server to return an array of players, populated with all the player’s fields. The limit parameter will cause the server to return at most 10 records. The offset parameter can be specified as a parameter to the query. This will allow paging through the players.

@Component({
  selector: 'app-players',
  templateUrl: './players.component.html',
  styleUrls: ['./players.component.css']
})
export class PlayersComponent implements OnInit {
  page = 0;
  players: any[] = [];

  private query: QueryRef<any>;

  constructor(private apollo: Apollo) {}
}


The properties of PlayersComponent specify the state of the component. The property page stores the current page in the list of players. players will contain the array of players that will be displayed in a table. There is also a query variable which stores the query. This is needed to be able to re-fetch data, whenever the user navigates to another page. The constructor will inject the apollo property so that you can access the GraphQL interface.

ngOnInit() {
  this.query = this.apollo
    .watchQuery({
      query: PLAYERS_QUERY,
      variables: {offset : 10*this.page}
    });

    this.query.valueChanges.subscribe(result => {
      this.players = result.data && result.data.players;
    });
  }


During the initialization phase of the component’s life-cycle the ngOnInit method will be called. This is the place where the Players Component will initiate the loading of the data. This is achieved by this.apollo.watchQuery. By passing the PLAYERS_QUERY together with a value for the offset parameter. You can now subscribe to any data changes using valueChanges.subscribe. This method takes a callback which will set the players array with the data obtained from the server.

update() {
  this.query.refetch({offset : 10*this.page});
}

nextPage() {
  this.page++;
  this.update();
}

prevPage() {
  if (this.page>0) this.page--;
  this.update();
}


To round things off, nextPage and prevPage will increment or decrement the page property. By calling refetch on query with the new parameters a server request is issued. When the data is received the subscription callback will be called automatically.

The HTML template that goes with this component is stored in players.component.html. Paste the following content into it.

<table
  class="mdl-data-table mdl-js-data-table mdl-data-table--selectable mdl-shadow--2dp"
>
  <tr>
    <th class="mdl-data-table__cell--non-numeric">First Name</th>
    <th class="mdl-data-table__cell--non-numeric">Last Name</th>
    <th class="mdl-data-table__cell--non-numeric">Hand</th>
    <th>Birthday</th>
    <th class="mdl-data-table__cell--non-numeric">Country</th>
  </tr>
  <tr *ngFor="let player of players">
    <td class="mdl-data-table__cell--non-numeric">{{player.first_name}}</td>
    <td class="mdl-data-table__cell--non-numeric">{{player.last_name}}</td>
    <td class="mdl-data-table__cell--non-numeric">{{player.hand}}</td>
    <td>{{player.birthday}}</td>
    <td class="mdl-data-table__cell--non-numeric">{{player.country}}</td>
  </tr>
</table>

<div class="paging">
  <button
    class="mdl-button mdl-js-button mdl-button--colored"
    (click)="prevPage()"
  >
    <i class="material-icons">arrow_back</i>
  </button>
  Page {{page+1}}
  <button
    class="mdl-button mdl-js-button mdl-button--colored"
    (click)="nextPage()"
  >
    <i class="material-icons">arrow_forward</i>
  </button>
</div>


This displays a list of players in a table. Below the table, I have added paging links.

The Ranking component pretty much follows the same pattern. The src/app/ranking.component.ts looks like this.

import { Component, OnInit } from '@angular/core';
import { Apollo, QueryRef } from 'apollo-angular';
import gql from 'graphql-tag';

const RANKINGS_QUERY = gql`
  query rankings($rank: Int!) {
    rankings(rank: $rank) {
      date
      rank
      points
      player {
        first_name
        last_name
      }
    }
  }
`;

@Component({
  selector: 'app-ranking',
  templateUrl: './ranking.component.html',
  styleUrls: ['./ranking.component.css']
})
export class RankingComponent implements OnInit {
  rank: number = 1;
  rankings: any[];
  private query: QueryRef<any>;

  constructor(private apollo: Apollo) {}

  ngOnInit() {
    this.query = this.apollo.watchQuery({
      query: RANKINGS_QUERY,
      variables: { rank: Math.round(this.rank) }
    });

    this.query.valueChanges.subscribe(result => {
      this.rankings = result.data && result.data.rankings;
    });
  }

  update() {
    return this.query.refetch({ rank: Math.round(this.rank) });
  }
}


As you can see, most of the code is very similar to that in players.component.ts. The definition of RANKINGS_QUERY queries the players over time which held a particular rank. Note that the query is only requesting the first_name and last_name of the player. This means that the server will not be sending any additional player data back which the client hasn’t asked for.

The template for the rankings component contains a text field and button in which the user can enter a rank and reload the page. Below that is the table of players. This is the content of ranking.component.html.

<h1>Rankings</h1>
<input class="mdl-textfield__input" type="text" id="rank" [(ngModel)]="rank" />

<button
  class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect"
  (click)="update()"
>
  Update
</button>
<table
  class="mdl-data-table mdl-js-data-table mdl-shadow--2dp"
  *ngIf="rankings"
>
  <tr>
    <th>Rank</th>
    <th>Date</th>
    <th>Points</th>
    <th class="mdl-data-table__cell--non-numeric">First Name</th>
    <th class="mdl-data-table__cell--non-numeric">Last Name</th>
  </tr>
  <tr *ngFor="let ranking of rankings">
    <td>{{ranking.rank}}</td>
    <td>{{ranking.date}}</td>
    <td>{{ranking.points}}</td>
    <td class="mdl-data-table__cell--non-numeric">
      {{ranking.player.first_name}}
    </td>
    <td class="mdl-data-table__cell--non-numeric">
      {{ranking.player.last_name}}
    </td>
  </tr>
</table>


To start the client, run:

ng serve


Make sure that the server is also running, so that the client can successfully request data.

Add Access Control to your Express + Angular GraphQL App

One of the most important features of every web application is user authentication and access control. In this section, I will guide you through the steps needed to add authentication to both the server and the client part of your Angular application. This is often the most daunting part of writing an application. Using Okta greatly simplifies this task and makes secure authentication available to every developer. If you haven’t already done so, create a developer account with Okta. Visit https://developer.okta.com/ and select Create Free Account.

Fill out the form and register yourself. After your registration is complete you can see your developer dashboard.

From the top menu of your dashboard, select Applications and then add an application by clicking the green Add Application button.

You will see a choice of different types of application. You are registering a Single Page App. On the next page, you will see the settings for your application. Here the port number is pre-filled to 8080. Angular uses the port 4200 by default. So you will have to change the port number to 4200.

Once completed, you will be given a ClientId. You will need this in both your client and server applications. You will also need your Okta developer domain. This is the URL that you see at the top of the page when you are logged in to your Okta developer dashboard.

Secure Your Angular Client

In order to use Okta authentication with the Angular client, you will have to install the okta-angular library. In the base directory of your client application run the following command.

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


Now open src/app/app.module.ts. At the top of the file add the import statement.

import { OktaAuthModule } from '@okta/okta-angular';


Now add the module to the list of imports of the app module.

OktaAuthModule.initAuth({
  issuer: 'https://{yourOktaDomain}/oauth2/default',
  redirectUri: 'http://localhost:4200/implicit/callback',
  clientId: '{yourClientId}'
});


You will need to replace yourOktaDomain development domain you see in your browser when you navigate to your Okta dashboard. Also, replace yourClientId with the client ID that you obtained when registering your application. Now you are ready to use Okta authentication throughout your application. Next, you will implement logging in and logging out from the application. Open app.component.ts and import OktaAuthService from okta-angular. Paste the following code into the file.

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { OktaAuthService } from '@okta/okta-angular';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  public title = 'My Angular App';
  public isAuthenticated: boolean;

  constructor(public oktaAuth: OktaAuthService) {
    this.oktaAuth.$authenticationState.subscribe(
      (isAuthenticated: boolean) => (this.isAuthenticated = isAuthenticated)
    );
  }

  async ngOnInit() {
    this.isAuthenticated = await this.oktaAuth.isAuthenticated();
  }

  login() {
    this.oktaAuth.loginRedirect();
  }

  logout() {
    this.oktaAuth.logout('/');
  }
}


The OktaAuthService service is injected through the constructor. It is then used to set the isAuthenticated flag. The subscribe method subscribes a callback function that is triggered whenever the log-in status changes. The isAuthenticated is initialized during the ngOnInit phase to reflect the log-in status when the application is first loaded. login and logout handle the process of logging in and out. In order to make authentication work, okta-angular uses a special route called implicit/callback. In the file app-routing.module.ts add the following import.

import { OktaCallbackComponent, OktaAuthGuard } from '@okta/okta-angular';


The implicit/callback route is now linked to the OktaCallbackComponent by adding the following to the routes array.

{
  path: 'implicit/callback',
  component: OktaCallbackComponent
}


This is all that is needed to log in and out. But the application is not protected yet. For any route that you want to access-control, you will have to add an Authorization Guard. Luckily this is easy. In each of the routes that you want to protect add the canActivate property. Add the following to the players and the ranking routes.

canActivate: [OktaAuthGuard];


That’s all there is to it. Now, when a user tries to access the Players view, he will be redirected to the Okta login page. Once logged on, the user will be redirected back to the Products view.

You have secured the client pages, but before you can move on to securing the back end let’s take a moment and think about how the server will authenticate the user. Okta uses a bearer token that identifies the user. The bearer token must be sent to the server with every request. To achieve this, the client has to make sure that the bearer token is added to the HTTP headers. All you need to do is add a few lines of code to the graphql.module.ts. At the top of the file import the following.

import { OktaAuthService } from '@okta/okta-angular';
import { setContext } from 'apollo-link-context';


Then modify the createApollo function to add the bearer token.

export function createApollo(httpLink: HttpLink, oktaAuth: OktaAuthService) {
  const http = httpLink.create({ uri });

  const auth = setContext((_, { headers }) => {
    return oktaAuth.getAccessToken().then(token => {
      return token ? { headers: { Authorization: `Bearer ${token}` } } : {};
    });
  });

  return {
    link: auth.concat(http),
    cache: new InMemoryCache()
  };
}


Secure Your Express GraphQL Server

Securing the server is done by adding an express middleware function to the server application. To do this you will need a few additional libraries. Change into your server directory and run the command

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


Next, let’s create that function in a separate file called auth.js in the root folder of the server.

const OktaJwtVerifier = require('@okta/jwt-verifier');

const oktaJwtVerifier = new OktaJwtVerifier({
  clientId: '{yourClientId}',
  issuer: 'https://{yourOktaDomain}/oauth2/default'
});

module.exports = async function oktaAuth(req, res, next) {
  try {
    const token = req.token;
    if (!token) {
      return res.status(401).send('Not Authorized');
    }
    const jwt = await oktaJwtVerifier.verifyAccessToken(token);
    req.user = {
      uid: jwt.claims.uid,
      email: jwt.claims.sub
    };
    next();
  } catch (err) {
    return res.status(401).send(err.message);
  }
};


Again, you have to replace yourOktaDomain and yourClientId with the development domain and the client id. The purpose of this function is simple. It checks the presence of a token field in the request. If present, oktaJwtVerifier checks the validity of the token. If everything is in order, calling next() signals success. Otherwise, a 401 error is returned. All you have to do now is to make sure that the function is used in the application. Add the following require statements to the index.js file.

const bodyParser = require('body-parser');
const bearerToken = require('express-bearer-token');
const oktaAuth = require('./auth');


Then modify the declaration of app in the following way.

const app = express()
  .use(cors())
  .use(bodyParser.json())
  .use(bearerToken())
  .use(oktaAuth);


The bearerToken middleware will look for a bearer token and add it to the request for oktaAuth to find it. With this simple addition, your server will only allow requests that provide a valid authentication.

Learn More

Angular 7 (formerly Angular 2) - The Complete Guide

Learn and Understand AngularJS

Angular Crash Course for Busy Developers

The Complete Angular Course: Beginner to Advanced

Angular (Angular 2+) & NodeJS - The MEAN Stack Guide

Become a JavaScript developer - Learn (React, Node,Angular)

Angular (Full App) with Angular Material, Angularfire & NgRx

The Web Developer Bootcamp

The Complete Python & PostgreSQL Developer Course

SQL & Database Design A-Z™: Learn MS SQL Server + PostgreSQL

The Complete SQL Bootcamp

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.

Angular 7 CRUD with Nodejs and MySQL Example

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

Angular CRUD with Nodejs and MySQL Example

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

Angular 8 Tutorial By Example: (REST API, HttpClient GET, Components, Services & ngFor)

Angular 8 Tutorial By Example: (REST API, HttpClient GET, Components, Services & ngFor)

Angular 8 Tutorial By Example: (REST API, HttpClient GET, Components, Services & ngFor) - In this tutorial, you'll learn by example how to send GET requests to REST API servers in your Angular 8 application using HttpClient...

Angular 8 Tutorial By Example: (REST API, HttpClient GET, Components, Services & ngFor) - In this tutorial, you'll learn by example how to send GET requests to REST API servers in your Angular 8 application using HttpClient...

We’ll also learn how to use the basic concepts of Angular like components and services and how to use the ngFor directive to display collections of data.

We’ll be consuming a JSON API available from NewsAPI.org

Throughout this tutorial, we are going to build a simple example from scratch using Angular CLI 8 and we’ll see how to use HttpClient to send GET requests to third-party REST API servers and how to consume and display the returned JSON data.

In more details, we'll learn:

  • How to create an Angular 8 project using Angular CLI,
  • How to quickly set up routing in our project,
  • How to create Angular components and services,
  • How to subscribe to Observables,
  • How to use the ngFor directive in templates to iterate over data.

You can also follow this tutorial as a video:

Prerequisites

Before getting started, you need a few requirements. You need to have the following tools installed on your development machine:

  • Node.js and npm. You can install both of them from the official website.
  • Angular CLI 8 (You can install it from npm using: npm install -g @angular/cli)
Creating an Angular 8 Project

Now let’s create our Angular 8 project. Open a new terminal and run the following command:

$ ng new angular-httpclient-demo

The CLI will prompt you if Would you like to add Angular routing? (y/N), type y. And Which stylesheet format would you like to use? Choose CSS and type Enter.

Next, you can serve your application locally using the following commands:

$ cd ./angular-httpclient-demo
$ ng serve

Your application will be running from <a href="http://localhost:4200" target="_blank">http://localhost:4200</a>.

Getting News Data

Before you can fetch the news data from NewsAPI.org which offers a free plan for open source and development projects, you first need to go the register page for getting an API key.

Adding an Angular Service

Next, let’s create a service that will take care of getting data from the news API. Open a new terminal and run the following command:

$ ng generate service api

Setting up HttpClient

Next, open the src/app/app.module.ts file then import HttpClientModule and add it to the importsarray:

// [...]
import { HttpClientModule } from '@angular/common/http';

@NgModule({
  declarations: [AppComponent],
  entryComponents: [],
  imports: [
    // [...]
    HttpClientModule,
  ],
  // [...]
})
export class AppModule {}

That's all, we are now ready to use the HttpClient in our project.

Injecting HttpClient in The Angular Service

Next, open the src/app/api.service.ts file and inject HttpClient via the service constructor:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class ApiService {

  constructor(private httpClient: HttpClient) { }
}

Sending GET Request for Fetching Data

Next, define an API_KEY variable which will hold your API key from the News API:

export class ApiService {
  API_KEY = 'YOUR_API_KEY';

Finally, add a method that sends a GET request to an endpoint for TechCrunch news:

  public getNews(){
    return this.httpClient.get(`https://newsapi.org/v2/top-headlines?sources=techcrunch&apiKey=${this.API_KEY}`);
  }

That’s all we need to add for the service.

How the <strong>HttpClient.get()</strong> Method Works

The HttpClient get() method is designed to send HTTP GET requests. The syntax is as follows:

get(url: string, options: {
      headers?: HttpHeaders;
      observe: 'response';
      params?: HttpParams;
      reportProgress?: boolean;
      responseType?: 'json';
      withCredentials?: boolean;
}): Observable<HttpResponse<Object>>;

It takes a REST API endpoint and an optional options object and returns an Observable instance.

Creating an Angular 8 Component

Now, let's create an Angular 8 component for displaying the news data. Head back to your terminal and run the following command:

$ ng generate component news

Injecting ApiService in Your Component

Next, open the src/app/news/news.component.ts file and start by importing ApiService in your component:

import { ApiService } from '../api.service';

Next, you need to inject ApiService via the component's constructor:

import { Component, OnInit } from '@angular/core';
import { ApiService } from '../api.service';
@Component({
  selector: 'app-news',
  templateUrl: './news.component.html',
  styleUrls: ['./news.component.css']
})
export class NewsComponent implements OnInit {

  constructor(private apiService: ApiService) { }
}

Sending the GET Request & Subscribing to The Observable

Next, define an articles variable and call the getNews() method of the API service in the ngOnInit()method of the component:

export class NewsComponent implements OnInit {
  articles;

  constructor(private apiService: ApiService) { }
  ngOnInit() {
    this.apiService.getNews().subscribe((data)=>{
      console.log(data);
      this.articles = data['articles'];
    });
  }
}

This will make sure our data is fetched once the component is loaded.

We call the getNews() method and subscribe to the returned Observable which will send a GET request to the news endpoint.

Displaying Data in The Template with NgFor

Let’s now display the news articles in our component template. Open the src/app/news.component.htmlfile and update it as follows:

<div *ngFor="let article of articles">
  <h2>{{article.title}}</h2>

    <p>
      {{article.description}}
    </p>
    <a href="{{article.url}}">Read full article</a>
</div>

Adding the Angular Component to The Router

Angular CLI 8 has automatically added routing for us, so we don’t need to set up anything besides adding the component(s) to our Router configuration. Open the src/app/app-routing.module.ts file and start by importing the news component as follows:

import { NewsComponent } from './news/news.component';

Next, add the component to the routes array:

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

You can now access your component from the /news path.

Conclusion

In this tutorial, we used Angular 8 to build a simple news application that retrieves data from a JSON REST API using the get() method of HttpClient. We’ve seen how to subscribe to the RxJS Observable returned by the get() method and how to use the *ngFor directive to iterate over fetched data in the template. Finally, we’ve seen how we can create an Angular 8 project using Angular CLI v8, how to generate components and services and how to configure routing for the component.

Angular 8 Forms Tutorial - Reactive Forms Validation Example

Angular 8 Forms Tutorial - Reactive Forms Validation Example

In this article, you''ll see a quick example of how to setup form validation in Angular 8 using Reactive Forms.

In this article, you''ll see a quick example of how to setup form validation in Angular 8 using Reactive Forms.

The example is a simple registration form with pretty standard fields for title, first name, last name, email, password, confirm password and an accept Ts & Cs checkbox. All fields are required including the checkbox, the email field must be a valid email address and the password field must have a min length of 6. There's also a custom validator called MustMatch which is used to validate that the confirm password and password fields match.

I've setup the form to validate on submit rather than as soon as each field is changed, this is implemented with a submitted property in the app component that is set to true when the form is submitted for the first time, and reset to false if the cancel button is clicked.

Styling of the example is all done with Bootstrap 4.3 CSS.

See on StackBlitz at https://stackblitz.com/edit/angular-8-reactive-form-validation

Reactive Forms Validation App Component

The app component defines the form fields and validators for our registration form using an Angular FormBuilder to create an instance of a FormGroup that is stored in the registerForm property. The registerForm is then bound to the form in the app template below using the [formGroup] directive.

I also added a getter f as a convenience property to make it easier to access form controls from the template. So for example you can access the confirmPassword field in the template using f.confirmPassword instead of registerForm.controls.confirmPassword.

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

// import custom validator to validate that password and confirm password fields match
import { MustMatch } from './_helpers/must-match.validator';

@Component({ selector: 'app', templateUrl: 'app.component.html' })
export class AppComponent implements OnInit {
    registerForm: FormGroup;
    submitted = false;

    constructor(private formBuilder: FormBuilder) { }

    ngOnInit() {
        this.registerForm = this.formBuilder.group({
            title: ['', Validators.required],
            firstName: ['', Validators.required],
            lastName: ['', Validators.required],
            email: ['', [Validators.required, Validators.email]],
            password: ['', [Validators.required, Validators.minLength(6)]],
            confirmPassword: ['', Validators.required],
            acceptTerms: [false, Validators.requiredTrue]
        }, {
            validator: MustMatch('password', 'confirmPassword')
        });
    }

    // convenience getter for easy access to form fields
    get f() { return this.registerForm.controls; }

    onSubmit() {
        this.submitted = true;

        // stop here if form is invalid
        if (this.registerForm.invalid) {
            return;
        }

        // display form values on success
        alert('SUCCESS!! :-)\n\n' + JSON.stringify(this.registerForm.value, null, 4));
    }

    onReset() {
        this.submitted = false;
        this.registerForm.reset();
    }
}
Reactive Forms Validation App Template

The app component template contains all the html markup for displaying the example registration form in your browser. The form element uses the [formGroup] directive to bind to the registerForm FormGroup in the app component above.

The form binds the form submit event to the onSubmit() handler in the app component using the Angular event binding (ngSubmit)="onSubmit()". Validation messages are displayed only after the user attempts to submit the form for the first time, this is controlled with the submitted property of the app component.

The cancel button click event is bound to the onReset() handler in the app component using the Angular event binding (click)="onReset()".



    ##### Angular 8 Reactive Form Validation

    
        
            
                
                    Title
                    
                        
                        Mr
                        Mrs
                        Miss
                        Ms
                    
                    
                        Title is required

                    
                
                
                    First Name
                    
                    
                        First Name is required

                    
                
                
                    Last Name
                    
                    
                        Last Name is required

                    
                
            
            
                Email
                
                
                    Email is required

                    Email must be a valid email address

                
            
            
                
                    Password
                    
                    
                        Password is required

                        Password must be at least 6 characters

                    
                
                
                    Confirm Password
                    
                    
                        Confirm Password is required

                        Passwords must match

                    
                
            
            
                
                Accept Terms & Conditions
                Accept Ts & Cs is required

            
            
                Register
                Cancel
            
        
    

Reactive Forms Custom "Must Match" Validator

The custom MustMatch validator is used in this example to validate that both of the password fields - password and confirmPassword - are matching. However it can be used to validate that any pair of fields is matching (e.g. email and confirm email fields).

It works slightly differently than a typical custom validator because I'm setting the error on the second field instead of returning it to be set on the formGroup. I did it this way because I think it makes the template a bit cleaner and more intuitive, the mustMatch validation error is displayed below the confirmPassword field so I think it makes sense that the error is attached the the confirmPassword form control.

import { FormGroup } from '@angular/forms';

// custom validator to check that two fields match
export function MustMatch(controlName: string, matchingControlName: string) {
    return (formGroup: FormGroup) => {
        const control = formGroup.controls[controlName];
        const matchingControl = formGroup.controls[matchingControlName];

        if (matchingControl.errors && !matchingControl.errors.mustMatch) {
            // return if another validator has already found an error on the matchingControl
            return;
        }

        // set error on matchingControl if validation fails
        if (control.value !== matchingControl.value) {
            matchingControl.setErrors({ mustMatch: true });
        } else {
            matchingControl.setErrors(null);
        }
    }
}
Reactive Forms Validation App Module

There isn't much going on in the app module other than the standard stuff, the main thing you need to remember for using reactive forms in Angular is to import the ReactiveFormsModule from '@angular/forms' and include it in the imports array of the @NgModule decorator.

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

import { AppComponent } from './app.component';

@NgModule({
    imports: [
        BrowserModule,
        ReactiveFormsModule
    ],
    declarations: [
        AppComponent
    ],
    bootstrap: [AppComponent]
})
export class AppModule { }

Introduction New Features in TypeScript 3.7 and How to Use Them

Introduction New Features in TypeScript 3.7 and How to Use Them

The TypeScript 3.7 release is coming soon, and it's going to be a big one.

The target release date is November 5th, and there are some seriously exciting headline features included:

  • Assert signatures.
  • Recursive type aliases.
  • Top-level await.
  • Null coalescing.
  • Optional chaining.

Personally, I'm super excited about this, they're going to whisk away all sorts of annoyances that I've been fighting in TypeScript while building HTTP Toolkit.

If you haven't been paying close attention to the TypeScript development process though, it's probably not clear what half of these mean, or why you should care. Let's talk through all of them.

Assert Signatures

This is a brand-new and little-known TypeScript feature, which allows you to write functions that act like type guards as a side-effect, rather than explicitly returning their boolean result.

It's easiest to demonstrate this with a JavaScript example:

function assertString(input) { 
  if (typeof input === 'string') 
    return; 
  else 
    throw new Error('Input must be a string!'); 
} 
function doSomething(input) { 
  assertString(input); 
  // ... Use input, confident that it's a string 
} 
doSomething('abc'); 
// All good doSomething(123); // Throws an error

This pattern is neat and useful, and you can't use it in TypeScript today.

TypeScript can't know that you've guaranteed the type of input after it's run assertString. Typically, people just make the argument input: string to avoid this, and that's good. But, it also just pushes the type checking problem somewhere else, and in cases where you just want to fail hard, it's useful to have this option available.

Fortunately, soon we will:

// With TS 3.7 
function assertString(input: any): 
	asserts input is string { 
      // <-- the magic 
      if (typeof input === 'string') 
        return; 
      else 
        throw new Error('Input must be a string!'); 
    } 
function doSomething(input: string | number) { 
  assertString(input); 
  // input's type is just 'string' here }

Here assert input is string means that if this function ever returns, TypeScript can narrow the type of input to string, just as if it was inside an if block with a type guard.

To make this safe, that means if the assert statement isn't true then your assert function must either throw an error or not return at all (kill the process, infinite loop, you name it).

That's the basics, but this actually lets you pull some really neat tricks:

// With TS 3.7 
// Asserts that input is truthy, throwing immediately if not: 
function assert(input: any): 
	asserts input { // <-- not a typo 
      if (!input) 
        throw new Error('Not a truthy value'); 
    } 
declare const x: number | string | undefined; 
assert(x); // Narrows x to number | string 
// Also usable with type guarding expressions! 
assert(typeof x === 'string'); 
// Narrows x to string // -- Or use assert in your tests: -- 
const a: Result | Error = doSomethingTestable(); 
expect(a).is.instanceOf(result); 
// 'instanceOf' could 'asserts a is Result' 
expect(a.resultValue).to.equal(123); 
// a.resultValue is now legal // -- Use as a safer ! that throws immediately if 
// you're wrong -- 
function assertDefined<T>(obj: T): 
	asserts obj is NonNullable<T> { 
      if (obj === undefined || obj === null) { 
        throw new Error('Must not be a nullable value'); 
      } 
    } 
declare const x: string | undefined; 
// Gives y just 'string' as a type, could throw elsewhere later: 
const y = x!; 
// Gives y 'string' as a type, or throws immediately if you're wrong: 
assertDefined(x); const z = x; 
// -- Or even update types to track a function's side-effects -- 
type X<T extends string | {}> = { value: T }; 
// Use asserts to narrow types according to side effects: 
function setX<T extends string | {}>(x: X<any>, v: T): 
	asserts x is X<T> { 
      x.value = v; 
    } 
	declare let x: X<any>; 
// x is now { value: any }; 
setX(x, 123); 
// x is now { value: number };

This is still in flux, so don't take it as the definite result, and keep an eye on the pull request if you want the final details.

There's even a discussion there about allowing functions to assert something and return a type, which would let you extend the final example above to track a much wider variety of side effects, but we'll have to wait and see how that plays out.

Top-Level Await

Async/await is amazing and makes promises dramatically cleaner to use.

Unfortunately, though, you can't use them at the top level. This might not be something you care about much in a TS library or application, but if you're writing a runnable script or using TypeScript in a REPL, then this gets super annoying.

It's even worse if you're used to frontend development, since top-level await has been working nicely in the Chrome and Firefox console for a couple of years now.

Fortunately though, a fix is coming. This is actually a general stage-3 JS proposal, so it'll be everywhere else eventually too, but for TS devs 3.7 is where the magic happens.

This one's simple, but let's have another quick demo anyway:


// Your only solution right now for a script that does something async: 
async function doEverything() { 
  ... 
  const response = await fetch('http://example.com'); 
  ... 
} 
  
doEverything(); // <- eugh (could use an IIFE instead, but even more eugh)

With top-level await:

// With TS 3.7: 
// Your script: ... 
const response = await fetch('http://example.com'); 
// ...

There's a notable gotcha here: if you're not writing a script, or using a REPL, don't write this at the top level, unless you really know what you're doing!

It's totally possible to use this to write modules that do blocking async steps when imported. That can be useful for some niche cases, but people tend to assume that their import statement is a synchronous, reliable, and fairly quick operation, and you could easily hose your codebase's startup time if you start blocking imports for complex async processes (even worse, processes that can fail).

This is somewhat mitigated by the semantics of imports of async modules: they're imported and run in parallel, so the importing module effectively waits for Promise.all(importedModules) before being executed.

Rich Harris wrote an excellent piece on a previous version of this spec before that change when imports ran sequentially and this problem was much worse), which makes for good background reading on the risks here if you're interested.

It's also worth noting that this is only useful for module systems that support asynchronous imports. There isn't yet a formal spec for how TS will handle this, but that likely means that a very recent target configuration, and, either ES Modules or Webpack v5 (whose alphas have experimental support), will be used at runtime.

Recursive Type Aliases

If you're ever tried to define a recursive type in TypeScript, you may have run into StackOverflow questions like this: https://stackoverflow.com/questions/47842266/recursive-types-in-typescript.

Right now, you can't. Interfaces can be recursive, but there are limitations to their expressiveness, and type aliases can't. That means right now, you need to combine the two: define a type alias and extract the recursive parts of the type into interfaces. It works, but it's messy, and we can do better.

As a concrete example, this is the suggested type definition for JSON data:

type JSONValue = | string | number | boolean | JSONObject | JSONArray; 
interface JSONObject { [x: string]: JSONValue; } 
interface JSONArray extends Array<JSONValue> { }

That works, but the extra interfaces are only there because they're required to get around the recursion limitation.

Fixing this requires no new syntax; it just removes that restriction, so the below compiles:

// With TS 3.7: 
type JSONValue = | string | number | boolean | { [x: string]: JSONValue } | Array<JSONValue>;

Right now, that fails to compile with Type alias 'JSONValue' circularly references itself. Soon though, soon...

Null Coalescing

Aside from being difficult to spell, this one is quite simple and easy. It's based on a JavaScript stage-3 proposal, which means it'll also be coming to your favorite vanilla JavaScript environment soon (if it hasn't already).

In JavaScript, there's a common pattern for handling default values, and falling back to the first valid result of a defined group. It looks something like this:

// Use the first of firstResult/secondResult which is truthy: 
const result = firstResult || secondResult; 
// Use configValue from provided options if truthy, or 'default' if not: 
this.configValue = options.configValue || 'default';

This is useful in a host of cases, but due to some interesting quirks in JavaScript, it can catch you out. If firstResult or options.configValue can meaningfully be set to false, an empty string or 0, then this code has a bug. If those values are set, then when considered as booleans they're falsy, so the fallback value (secondResult / 'default') is used anyway.

Null coalescing fixes this. Instead of the above, you'll be able to write:

// With TS 3.7: 
// Use the first of firstResult/secondResult which is *defined*: 
const result = firstResult ?? secondResult; 
// Use configSetting from provided options if *defined*, or 'default' if not: 
this.configValue = options.configValue ?? 'default';

?? differs from || in that it falls through to the next value only if the first argument is null or undefined, not falsy. That fixes our bug. If you pass false as firstResult, that will be used instead of secondResult because, while it's falsy, it is still defined, and that's all that's required.

It's simple but super-useful, as it takes away a whole class of bugs.

Optional Chaining

Last but not least, optional chaining is another stage-3 proposal that is making its way into TypeScript.

This is designed to solve an issue faced by developers in every language: how do you get data out of a data structure when some or all of it might not be present?

Right now, you might do something like this:

// To get data.key1.key2, if any level could be null/undefined: 
let result = data ? (data.key1 ? data.key1.key2 : undefined) : undefined; 
// Another equivalent alternative: 
let result = ((data || {}).key1 || {}).key2;

Nasty! This gets much much worse if you need to go deeper, and although the second example works at runtime, it won't even compile in TypeScript, since the first step could be {}, in which case key1 isn't a valid key at all.

This gets still more complicated if you're trying to get into an array, or there's a function call somewhere in this process.

There's a host of other approaches to this, but they're all noisy, messy & error-prone. With optional chaining, you can do this:

// With TS 3.7: 
// Returns the value is it's all defined & non-null, or undefined if not. 
let result = data?.key1?.key2; 
// The same, through an array index or property, if possible: 
array?.[0]?.['key']; 
// Call a method, but only if it's defined: 
obj.method?.(); 
// Get a property, or return 'default' if any step is not defined: 
let result = data?.key1?.key2 ?? 'default';

The last case shows how neatly some of these dovetails together: null coalescing + optional chaining is a match made in heaven.

One gotcha: this will return undefined for missing values, even if they were null, e.g. in cases like (null)?.key (returns undefined). A small point, but one to watch out for if you have a lot of null in your data structures.

That's the lot! That should outline all the essentials for these features, but there are lots of smaller improvements, fixes, and editor support improvements coming too, so take a look at the official roadmap if you want to get into the nitty-gritty.

Angular 8 Pagination Example and Tutorial

Angular 8 Pagination Example and Tutorial

Pagination is the best way to show huge number of records in series for any application. Also showing/fetching thousands of record at one go will affect the performance of the application.

Pagination is the best way to show huge number of records in series for any application. Also showing/fetching thousands of record at one go will affect the performance of the application.

For example, when you search something that returns a large number of records which cannot be shown on a single web page therefore, those records are part into number of pages that can be accessed through links via pagination structure.

So today in this demo we will discuss the simple pagination in Angular 8.

Step 1: Create a basic app with angular cli
ng new angular8-simple-pagination-example

By typing the above command we will see a basic angular app created on the current folder. So move to the created folder by typing **cd angular8-simple-pagination-example/. **You can check the newly created app by typing http://localhost:4200 on the browser.

Step 2: install ngx-pagination pagination dependency from terminal

So run the below command over terminal

npm install ngx-pagination --save

Step 3: Create dummy records for pagination

Now we will create static data to show the pagination. So lets have a look on the code under file **app.component.ts **

import { Component } from '@angular/core';
import {NgxPaginationModule} from 'ngx-pagination';
@Component({
   selector: 'app-root',
   templateUrl: './app.component.html',
   styleUrls: ['./app.component.css']
})
export class AppComponent {
   title = 'simple pagination demo';
   collection = [];
   constructor(){
     for(let i=1;i<=100;i++){
       let Obj = {'name': `Employee Name ${i}`,'code': `EMP00 ${i}`}
       this.collection.push(Obj);
     }
   }
}

In the above file, we can see that inside constructor we have created a loop for created dummy record for 100 employees having employee name & code for showing pagination.

Step 4: Import dependency in app.module.ts

Now let's have a look on the code inside **app.module.ts **where the ngx-pagination module has been imported

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
 
import { NgxPaginationModule } from 'ngx-pagination';
import { AppComponent } from './app.component';
 
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
NgxPaginationModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

Step 5: Update view from app.component.html

Now one last step needed to do is, add the below code anywhere inside app.component.html

*  Emp Name | Emp code
 {{item.name}} | {{item.code}} 


Now, we are done with all the needed steps for the pagination in our angular application.

Step 6: Run the app

Run the app over the terminal with npm start and check the app after typing the url http://localhost:4200/.** **A page will open like below:

Conclusion

By following these easy steps we can easily achieve the client side pagination in Angular 8 application. If you want to impliment server side pagination in angular8 Server Side Pagination in Angular Example and Tutorial . You can also find other demos of Angular Sample Application here to start working on enterprise level application. Click here to view more about the pagination package over npm.