Building a web app using Nestjs, Fastify, MongoDB and Angular 8

Building a web app using Nestjs, Fastify, MongoDB and Angular 8

In this article, you'll learn how to build the efficient, reliable and scalable server-side web app using Nestjs, Fastify, MongoDB and Angular 8

Learn to build the efficient, reliable and scalable server-side web app using Nestjs, Fastify, MongoDB and Angular 8. Nest.js is a complete development kit for building scalable server-side applications. The Fastify is alternative for Express.js as HTTP-Server dan claimed faster than Express.js. Angular 8 is the latest, smaller build and faster Angular version. So, this is the perfect combination for building better web apps.

As usual, we will learn to build the efficient, reliable and scalable server-side web app through the step by step from the scratch. Starting to build the backend application using Nestjs and Fastify then complete with Angular 8 as frontend application. The Angular 8 application access the data layer via RESTful API that provided by Nestjs which served by prefix URL /api. Nest.js accessing the MongoDB via Mongoose.js. All required libraries and modules that use by the backend already prepared very well by The Nestjs.

The following libraries, frameworks, and modules are required for this tutorial:

Before move further, make sure we have installed the Node.js and MongoDB server. To check if they were installed, type these commands in the terminal or command line.

node -v
v10.15.1
npm -v
6.9.0
mongo --version
MongoDB shell version: 3.2.11

Install Nestjs and Create a New Application

We will start the steps by building a backend or server-side application using Nestjs. For that, we have to install the Nest.js first by typing this command in the terminal or command line.

sudo npm i -g @nestjs/cli

You can run that command without sudo in the command line. Next, to create a new server-side application, type this command.

nest new nest-angular8

Next, go to the newly created project folder then run the application for the first time by type this command.

npm start

Open the browser then go to [http://localhost:3000](http://localhost:3000`) and you should see the text Hello world! in the web browser. Next, install the Fastify.js to increase the performance of Nestjs application because by default Nestjs using Express.js. Type this command to install it.

npm i --save @nestjs/platform-fastify

Next, open and edit src/main.ts then modify/add these imports.

import { NestFactory } from '@nestjs/core';
import {
  FastifyAdapter,
  NestFastifyApplication,
} from '@nestjs/platform-fastify';
import { AppModule } from './app.module';

Change the bootstrap function to these lines of codes.

async function bootstrap() {
  const app = await NestFactory.create(
    AppModule,
    new FastifyAdapter(),
  );
  await app.listen(3000);
}
bootstrap();

Now, the Fastify.js will run as the default engine for Nestjs application.

Install and Configure Mongoose.js

We will use Mongoose.js as ORM for MongoDB. For that, type this command to install the Nest.js Mongoose.js modules.

npm install --save @nestjs/mongoose mongoose

Next, create a database folder inside the src folder. We will create all folder and files manually, if you like, you can generate them using Nestjs CLI.

mkdir database

Add 2 files to the database folder.

touch src/database/database.module.ts
touch src/database/database.provider.ts

Next, open and edit src/database/database.provider.ts then add these lines of codes.

import * as mongoose from 'mongoose';

export const databaseProviders = [
    {
        provide: 'DATABASE_CONNECTION',
        useFactory: async (): Promise =>
        await mongoose.connect('mongodb://localhost/angular8-crud', { useNewUrlParser: true, useFindAndModify: false }),
    },
];

Provider in this database module is used for connecting the Nest application with MongoDB database. Next, open and edit src/database/database.module.ts then add these lines of codes.

import { Module } from '@nestjs/common';
import { databaseProviders } from './database.providers';

@Module({
    providers: [...databaseProviders],
    exports: [...databaseProviders],
})
export class DatabaseModule {}

Create a Mongoose Schema

The next steps to create a CRUD RESTful API is to create a Mongoose schema or model. We will make a modular application for each object, so, for the Article object, we will create a folder for it first.

mkdir src/article

Next, create a schema folder and a schema file inside that folder.

mkdir src/article/schemas
touch src/article/schemas/article.schemas.ts

Next, open and edit that file then add these lines of Typescript codes.

import * as mongoose from 'mongoose';

export const ArticleSchema = new mongoose.Schema({
    title: { type: String, required: true },
    author: { type: String, required: true },
    description: { type: String, required: true },
    content: { type: String, required: true },
    updatedAt: { type: Date, default: Date.now },
});

Next, create a provider file to register the Mongoose schema as a model.

touch src/article/article.providers.ts

Open and edit that file then add these lines of Typescript codes.

import { Connection } from 'mongoose';
import { ArticleSchema } from './schemas/article.schemas';

export const articleProviders = [
    {
        provide: 'ARTICLE_MODEL',
        useFactory: (connection: Connection) => connection.model('Article', ArticleSchema),
        inject: ['DATABASE_CONNECTION'],
    },
];

Create the Routes for Article

Creating RESTful API using Nestjs similar to ASP Net Core Web API, especially when interacting with the Database. We have to create DTO and Interface to access Mongoose schema. Next, create a folder and a file for DTO inside article folder.

mkdir src/article/dto
touch src/article/dto/article.dto.ts

Open and edit that file then add these lines of Typescript codes.

export class ArticleDto {
    readonly title: string;
    readonly author: string;
    readonly description: string;
    readonly content: string;
}

Next, create a folder and file for the interface inside the article folder.

mkdir src/article/interfaces
touch src/article/interfaces/article.interface.ts

Open and edit that file then add these lines of Typescript codes.

import { Document } from 'mongoose';

export interface Article extends Document {
    readonly title: string;
    readonly author: string;
    readonly description: string;
    readonly content: string;
}

That interface similar with the DTO except the interface extends Mongoose Document module. Next, we will create a service for CRUD operation of Mongoose schema. Create a new file for the service inside the article folder.

touch src/article/article.service.ts

Open and edit that file then add these lines of Typescript codes.

import { Inject, Injectable } from '@nestjs/common';
import { Model } from 'mongoose';
import { ArticleDto } from './dto/article.dto';
import { Article } from './interfaces/article.interface';

@Injectable()
export class ArticleService {

  constructor(@Inject('ARTICLE_MODEL') private readonly articleModel: Model) {}

  async create(articleDto: ArticleDto): Promise {
    const createdArticle = new this.articleModel(articleDto);
    return await createdArticle.save();
  }

  async findAll(): Promise {
    return await this.articleModel.find().exec();
  }

  async find(id: string): Promise {
    return await this.articleModel.findById(id).exec();
  }

  async update(id: string, articleDto: ArticleDto): Promise {
    return await this.articleModel.findByIdAndUpdate(id, articleDto);
  }

  async delete(id: string, articleDto: ArticleDto): Promise {
    return await this.articleModel.findByIdAndRemove(id);
  }
}

Creating the Nestjs routes means to create a controller that will transform by the Nest.js as a router. Type this command to create a file for the controller inside the article folder.

touch src/article/article.controller.ts

Open and edit that file then add these lines of Typescript for all Post, Put, Get, and Delete routes.

import { Controller, Get, Post, Put, Delete, Body, Param } from '@nestjs/common';
import { ArticleDto } from './dto/article.dto';
import { ArticleService } from './article.service';
import { Article } from './interfaces/article.interface';

@Controller('article')
export class ArticleController {
    constructor(private readonly articleService: ArticleService) {}

    @Post()
        async create(@Body() articleDto: ArticleDto) {
        return this.articleService.create(articleDto);
    }

    @Get()
        async findAll(): Promise {
        return this.articleService.findAll();
    }

    @Get(':id')
        async find(@Param('id') id: string) {
        return this.articleService.find(id);
    }

    @Put(':id')
        async update(@Param('id') id: string, @Body() articleDto: ArticleDto) {
        return this.articleService.update(id, articleDto);
    }

    @Delete(':id')
        async delete(@Param('id') id: string, @Body() articleDto: ArticleDto) {
        return this.articleService.delete(id, articleDto);
    }
}

The routes for Post, Get, Put, Delete, Param, and Body marked by Nestjs annotation. Next, create a file inside the article folder for the provider that register a connection from the schema to the Database that handles by database module.

touch src/article/article.providers.ts

Open and edit that file the add these lines of Typescript codes.

import { Connection } from 'mongoose';
import { ArticleSchema } from './schemas/article.schemas';

export const articleProviders = [
    {
        provide: 'ARTICLE_MODEL',
        useFactory: (connection: Connection) => connection.model('Article', ArticleSchema),
        inject: ['DATABASE_CONNECTION'],
    },
];

Next, wrapping all required files to build an article module as the RESTful API by creating a file inside the article folder for the module.

touch src/article/article.module.ts

Open and edit that file then add these lines of Typescript codes.

import { Module } from '@nestjs/common';
import { ArticleController } from './article.controller';
import { ArticleService } from './article.service';
import { articleProviders } from './article.providers';
import { DatabaseModule } from '../database/database.module';

@Module({
    imports: [DatabaseModule],
    controllers: [ArticleController],
    providers: [ArticleService, ...articleProviders],
})
export class ArticleModule {}

Finally, register the article module to the main Nestjs module. Open and edit src/app.module.ts then add this import.

import { ArticleModule } from './article/article.module';

Add to the @Module imports array.

@Module({
  imports: [ArticleModule],
})

We have to use different URL for the RESTful API. For that, create a prefix /api for the RESTful API by open and edit src/main.ts then add this line before the port declaration.

app.setGlobalPrefix('/api');

Test the Nestjs RESTful API using the Postman

Now, to test the Nestjs RESTful API, we will use the Postman application. First, run again the Nestjs application after running the MongoDB server in the different terminal tab.

npm start

Next, open the Postman application then make a GET request like below with the results.

Building a web app

To make a GET request for single result by ID, change the URL to this.

http://localhost:3000/api/article/5d234c34420cfcf038ee72c9

You should see the single result of the request like below.

Building a web app

To make a POST request, change the method to POST then address to

[http://localhost:3000/api/article.](http://localhost:3000/api/article`.) Fill the body as raw data with this

JSON data.

{
    "title": "A test article",
    "author": "Me",
    "description": "The article that live in the Nestjs server",
    "content": "The article that live in the Nestjs server."
}

You will see this response for the successful POST request.

{
    "_id": "5d28759e4f5e134b379e0dbb",
    "title": "A test article",
    "author": "Me",
    "description": "The article that live in the Nestjs server",
    "content": "The article that live in the Nestjs server.",
    "updatedAt": "2019-07-12T11:57:18.205Z",
    "__v": 0
}

For the PUT and DELETE method, just change the method in the Postman and the URL using the ID parameter. If everything works fine then the Nestjs RESTful API is ready to use with Angular 8 application.

Create a New Angular 8 Application

Now, for the frontend, we will create a new Angular 8 application. We will put the Angular 8 project folder inside the root of Nestjs folder. Before creating the new Angular 8 application, update the Angular CLI to the latest version.

sudo npm install -g @angular/cli

Check the installed or update Angular version using this command.

ng --version
Angular CLI: 8.0.6

Next, type this command to create a new Angular 8 application inside the Nestjs root folder.

ng new client

Go to the newly created Angular 8 project.

cd client

Run the Angular 8 application for the first time to make sure everything is on the track.

ng serve

You will see the Angular 8 started page in the browser when you point the browser address to [http://localhost:4200.](http://localhost:4200`.)

Create Angular 8 Routes for Page Navigation

To create Angular 8 Routes for navigation between Angular 8 pages/component, add or generate all required component.

ng g component articles
ng g component show-article
ng g component add-article
ng g component edit-article

Next, open and edit src/app/app-routing.module.ts then add these imports.

import { ArticlesComponent } from './articles/articles.component';
import { ShowArticleComponent } from './show-article/show-article.component';
import { AddArticleComponent } from './add-article/add-article.component';
import { EditArticleComponent } from './edit-article/edit-article.component';

Add the route from the Angular 8 component to the routes constant variable.

const routes: Routes = [
  {
    path: 'articles',
    component: ArticlesComponent,
    data: { title: 'List of Articles' }
  },
  {
    path: 'show-article/:id',
    component: ShowArticleComponent,
    data: { title: 'Show Product' }
  },
  {
    path: 'add-article',
    component: AddArticleComponent,
    data: { title: 'Add Article' }
  },
  {
    path: 'edit-article/:id',
    component: EditArticleComponent,
    data: { title: 'Edit Article' }
  },
  { path: '',
    redirectTo: '/articles',
    pathMatch: 'full'
  }
];

Open and edit src/app/app.component.html and you will see the existing router outlet. Next, modify this HTML page to fit the CRUD page.



Open and edit src/app/app.component.scss then replace all SASS codes with this.

.container {
  padding: 20px;
}

Create an Angular 8 Service using HttpClient, Observable and RXJS

To access the Nestjs RESTful API from Angular 8, we need to create an Angular 8 service which will handle all POST, GET, UPDATE, DELETE requests. The response from the RESTful API emitted by Observable that can subscribe and read from the Components. For error handler and data Extraction, we will use RXJS Library. Before creating a service for RESTful API access, first, we have to install or register HttpClientModule. Open and edit src/app/app.module.ts then add this import.

import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';

Add it to @NgModule imports after BrowserModule.

imports: [
  BrowserModule,
  FormsModule,
  HttpClientModule,
  AppRoutingModule
],

We will use type specifier to get a typed result object. For that, create a new Typescript file src/app/article.ts then add these lines of Typescript codes.

export class Article {
  _id: string;
  title: string;
  author: string;
  description: string;
  content: string;
  updatedAt: Date;
}

Next, generate an Angular 8 service by typing this command.

ng g service api

Next, open and edit src/app/api.service.ts then add these imports.

import { Observable, of, throwError } from 'rxjs';
import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
import { catchError, tap, map } from 'rxjs/operators';
import { Article } from './article';

Add these constants before the @Injectable.

const httpOptions = {
  headers: new HttpHeaders({'Content-Type': 'application/json'})
};
const apiUrl = '/api/article';

We are using URL for API using /api/article because at the end we will integrate the Angular 8 application as the static files for the Nestjs Fastify server. Next, inject HttpClient module to the constructor.

constructor(private http: HttpClient) { }

Add the error handler function.

private handleError(operation = 'operation', result?: T) {
  return (error: any): Observable => {
    console.error(error); // log to console instead
    return of(result as T);
  };
}

Add the functions for all CRUD (create, read, update, delete) RESTful call of article data.

getArticles(): Observable {
  return this.http.get(apiUrl)
    .pipe(
      tap(article => console.log('fetched articles')),
      catchError(this.handleError('getArticles', []))
    );
}

getArticle(id: number): Observable {
  const url = `${apiUrl}/${id}`;
  return this.http.get(url).pipe(
    tap(_ => console.log(`fetched article id=${id}`)),
    catchError(this.handleError(`getArticle id=${id}`))
  );
}

addArticle(article: Article): Observable {
  return this.http.post(apiUrl, article, httpOptions).pipe(
    tap((art: Article) => console.log(`added article w/ id=${art._id}`)),
    catchError(this.handleError('addArticle'))
  );
}

updateArticle(id: any, article: Article): Observable {
  const url = `${apiUrl}/${id}`;
  return this.http.put(url, article, httpOptions).pipe(
    tap(_ => console.log(`updated article id=${id}`)),
    catchError(this.handleError('updateArticle'))
  );
}

deleteArticle(id: any): Observable {
  const url = `${apiUrl}/${id}`;
  return this.http.delete(url, httpOptions).pipe(
    tap(_ => console.log(`deleted article id=${id}`)),
    catchError(this.handleError('deleteArticle'))
  );
}

You can find more details about Angular 8 Observable and RXJS here.

Display List of Articles using Angular 8 Material

We will display the list of articles published from API Service. The data published from the API service read by subscribing as an Article model in the Angular 8 component. For that, open and edit src/app/articles/articles.component.ts then add these imports.

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

Next, inject the API Service to the constructor.

constructor(private api: ApiService) { }

Next, for the user interface (UI) we will use Angular 8 Material and CDK. There's a CLI for generating a Material component like Table as a component, but we will create or add the Table component from scratch to existing component. Type this command to install Angular 8 Material.

ng add @angular/material

If there are questions like below, just use the default answer.

? Choose a prebuilt theme name, or "custom" for a custom theme: Purple/Green       [ Preview: h
ttps://material.angular.io?theme=purple-green ]
? Set up HammerJS for gesture recognition? Yes
? Set up browser animations for Angular Material? Yes

We will register all required Angular 8 Material components or modules to src/app/app.module.ts. Open and edit that file then add these imports.

import {
  MatInputModule,
  MatPaginatorModule,
  MatProgressSpinnerModule,
  MatSortModule,
  MatTableModule,
  MatIconModule,
  MatButtonModule,
  MatCardModule,
  MatFormFieldModule } from "@angular/material";

Also, modify FormsModule import to add ReactiveFormsModule.

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

Register the above modules to @NgModule imports.

imports: [
  BrowserModule,
  FormsModule,
  HttpClientModule,
  AppRoutingModule,
  ReactiveFormsModule,
  BrowserAnimationsModule,
  MatInputModule,
  MatTableModule,
  MatPaginatorModule,
  MatSortModule,
  MatProgressSpinnerModule,
  MatIconModule,
  MatButtonModule,
  MatCardModule,
  MatFormFieldModule
],

Next, back to src/app/articles/articles.component.ts then add these imports.

import { Article } from '../article';

Declare the variables of Angular 8 Material Table Data Source before the constructor.

displayedColumns: string[] = ['title', 'author'];
data: Article[] = [];
isLoadingResults = true;

Modify the ngOnInit function to get list of articles immediately.

ngOnInit() {
  this.api.getArticles()
    .subscribe((res: any) => {
      this.data = res;
      console.log(this.data);
      this.isLoadingResults = false;
    }, err => {
      console.log(err);
      this.isLoadingResults = false;
    });
}

Next, open and edit src/app/articles/articles.component.html then replace all HTML tags with this Angular Material tags.


  <div class="example-loading-shade"
       *ngIf="isLoadingResults">



    add


    <table mat-table [dataSource]="data" class="example-table"
           matSort matSortActive="title" matSortDisableClear matSortDirection="asc">



        Title
        {{row.title}}




        Author
        $ {{row.author}}






Finally, to make a little UI adjustment, open and edit src/app/articles/articles.component.scss then add this CSS codes.

/* Structure */
.example-container {
  position: relative;
  padding: 5px;
}

.example-table-container {
  position: relative;
  max-height: 400px;
  overflow: auto;
}

table {
  width: 100%;
}

.example-loading-shade {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 56px;
  right: 0;
  background: rgba(0, 0, 0, 0.15);
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}

.example-rate-limit-reached {
  color: #980000;
  max-width: 360px;
  text-align: center;
}

/* Column Widths */
.mat-column-number,
.mat-column-state {
  max-width: 64px;
}

.mat-column-created {
  max-width: 124px;
}

.mat-flat-button {
  margin: 5px;
}

Show and Delete an Article Details using Angular 8 Material

To show article details after click or tap on the one of a row inside the Angular 8 Material table, open and edit src/app/show-article/show-article.component.ts then add these imports.

import { ActivatedRoute, Router } from '@angular/router';
import { ApiService } from '../api.service';
import { Article } from '../article';

Inject above modules to the constructor.

constructor(private route: ActivatedRoute, private api: ApiService, private router: Router) { }

Declare the variables before the constructor for hold article data that get from the API.

getArticleDetails(id: any) {
  this.api.getArticle(id)
    .subscribe((data: any) => {
      this.article = data;
      console.log(this.article);
      this.isLoadingResults = false;
    });
}

Add a function for getting Article data from the API.

getArticleDetails(id: any) { this.api.getArticle(id)  .subscribe((data: any) => {   this.article = data;   console.log(this.article);   this.isLoadingResults = false;  }); }

Call that function when the component is initiated.

ngOnInit() {
  this.getArticleDetails(this.route.snapshot.params.id);
}

Add this function for delete article.

deleteArticle(id: any) {
  this.isLoadingResults = true;
  this.api.deleteArticle(id)
    .subscribe(res => {
        this.isLoadingResults = false;
        this.router.navigate(['/articles']);
      }, (err) => {
        console.log(err);
        this.isLoadingResults = false;
      }
    );
}

For the view, open and edit src/app/article-detail/article-detail.component.html then replace all HTML tags with this.


  <div class="example-loading-shade"
       *ngIf="isLoadingResults">



    list



      ## {{article.title}}

      {{article.author}} | {{article.updatedAt}}


      ### {{article.description}}


{{article.content}}



      edit
      delete


Finally, open and edit src/app/article-detail/article-detail.component.scss then add this lines of CSS codes.

/* Structure */
.example-container {
  position: relative;
  padding: 5px;
}

.example-loading-shade {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 56px;
  right: 0;
  background: rgba(0, 0, 0, 0.15);
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}

.mat-flat-button {
  margin: 5px;
}

Add an Article using Angular 8 Material

To create a form for adding an Article, open and edit src/app/add-article/add-article.component.ts then add these imports.

import { Router } from '@angular/router';
import { ApiService } from '../api.service';
import { FormControl, FormGroupDirective, FormBuilder, FormGroup, NgForm, Validators } from '@angular/forms';
import { ErrorStateMatcher } from '@angular/material/core';

Inject above modules to the constructor.

constructor(private router: Router, private api: ApiService, private formBuilder: FormBuilder) { }

Declare variables for the Form Group and all of the required fields inside the form before the constructor.

articleForm: FormGroup;
title = '';
author = '';
description = '';
content = '';
isLoadingResults = false;
matcher = new MyErrorStateMatcher();

Next, create a class for MyErrorStateMatcher before the the main class @Components.

/** Error when invalid control is dirty, touched, or submitted. */
export class MyErrorStateMatcher implements ErrorStateMatcher {
  isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
    const isSubmitted = form && form.submitted;
    return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted));
  }
}

Add initial validation for each field.

ngOnInit() {
  this.articleForm = this.formBuilder.group({
    'title' : [null, Validators.required],
    'author' : [null, Validators.required],
    'description' : [null, Validators.required],
    'content' : [null, Validators.required]
  });
}

Create a function for submitting or POST article form.

onFormSubmit() {
  this.isLoadingResults = true;
  this.api.addArticle(this.articleForm.value)
    .subscribe((res: any) => {
        const id = res._id;
        this.isLoadingResults = false;
        this.router.navigate(['/show-article', id]);
      }, (err: any) => {
        console.log(err);
        this.isLoadingResults = false;
      });
}

Next, open and edit src/app/add-article/add-article.component.html then replace all HTML tags with this.


  <div class="example-loading-shade"
       *ngIf="isLoadingResults">



    list




        <input matInput placeholder="Title" formControlName="title"
               [errorStateMatcher]="matcher">

          Please enter Title



        <input matInput placeholder="Author" formControlName="author"
               [errorStateMatcher]="matcher">

          Please enter Author



        <input matInput placeholder="Description" formControlName="description"
               [errorStateMatcher]="matcher">

          Please enter Description



        <textarea matInput placeholder="Content" formControlName="content"
               [errorStateMatcher]="matcher">

          Please enter Content



        save



Finally, open and edit src/app/article-add/article-add.component.scss then add this CSS codes.

/* Structure */
.example-container {
  position: relative;
  padding: 5px;
}

.example-form {
  min-width: 150px;
  max-width: 500px;
  width: 100%;
}

.example-full-width {
  width: 100%;
}

.example-full-width:nth-last-child(0) {
  margin-bottom: 10px;
}

.button-row {
  margin: 10px 0;
}

.mat-flat-button {
  margin: 5px;
}

Edit an Article using Angular 8 Material

We have put an edit button inside the Article Detail component for call Edit page. Now, open and edit src/app/edit-article/edit-article.component.ts then add these imports.

import { Router, ActivatedRoute } from '@angular/router';
import { ApiService } from '../api.service';
import { FormControl, FormGroupDirective, FormBuilder, FormGroup, NgForm, Validators } from '@angular/forms';
import { ErrorStateMatcher } from '@angular/material/core';

Next, create a class for ErrorStateMatcher before the main class @Compoents.

/** Error when invalid control is dirty, touched, or submitted. */
export class MyErrorStateMatcher implements ErrorStateMatcher {
  isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
    const isSubmitted = form && form.submitted;
    return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted));
  }
}

Inject the imported modules to the constructor.

constructor(private router: Router, private route: ActivatedRoute, private api: ApiService, private formBuilder: FormBuilder) { }

Declare the Form Group variable and all of the required variables for the articles form before the constructor.

articleForm: FormGroup;
_id = '';
title = '';
author = '';
description = '';
content = '';
isLoadingResults = false;
matcher = new MyErrorStateMatcher();

Next, add validation for all fields when the component is initiated.

ngOnInit() {
  this.getArticle(this.route.snapshot.params.id);
  this.articleForm = this.formBuilder.group({
    'title' : [null, Validators.required],
    'author' : [null, Validators.required],
    'description' : [null, Validators.required],
    'content' : [null, Validators.required]
  });
}

Create a function for getting article data that filled to each form fields.

getArticle(id: any) {
  this.api.getArticle(id).subscribe((data: any) => {
    this._id = data._id;
    this.articleForm.setValue({
      title: data.title,
      author: data.author,
      description: data.description,
      content: data.content
    });
  });
}

Create a function to update the article changes.

onFormSubmit() {
  this.isLoadingResults = true;
  this.api.updateArticle(this._id, this.articleForm.value)
    .subscribe((res: any) => {
        const id = res._id;
        this.isLoadingResults = false;
        this.router.navigate(['/show-article', id]);
      }, (err: any) => {
        console.log(err);
        this.isLoadingResults = false;
      }
    );
}

Add a function for handling the show article details button.

articleDetails() {
  this.router.navigate(['/show-article', this._id]);
}

Next, open and edit src/app/edit-article/edit-article.component.html then replace all HTML tags with this.


  <div class="example-loading-shade"
       *ngIf="isLoadingResults">



    info




        <input matInput placeholder="Title" formControlName="title"
               [errorStateMatcher]="matcher">

          Please enter Title



        <input matInput placeholder="Author" formControlName="author"
               [errorStateMatcher]="matcher">

          Please enter Author



        <input matInput placeholder="Description" formControlName="description"
               [errorStateMatcher]="matcher">

          Please enter Description



        <textarea matInput placeholder="Content" formControlName="content"
               [errorStateMatcher]="matcher">

          Please enter Content



        save



Finally, open and edit src/app/edit-article/edit-article.component.scss then add this lines of CSS codes.

/* Structure */
.example-container {
  position: relative;
  padding: 5px;
}

.example-form {
  min-width: 150px;
  max-width: 500px;
  width: 100%;
}

.example-full-width {
  width: 100%;
}

.example-full-width:nth-last-child(0) {
  margin-bottom: 10px;
}

.button-row {
  margin: 10px 0;
}

.mat-flat-button {
  margin: 5px;
}

Integrate, Test and Run the Complete Nestjs, Fastify, MongoDB and Angular 8 Web Apps

To integrate the Angular 8 application into the Nestjs/Fastify application, open and edit src/main.js then add this import.

import { join } from 'path';

Add this line before the port listener line.

app.useStaticAssets({
  root: join(__dirname, '..', 'client/dist/client'),
  prefix: '/',
});

Next, make sure the MongoDB server is running then build the Angular 8 application.

cd client
ng build --prod

The Angular 8 build should be located in client/dist/client folder. Next, back to the root of Nestjs folder then run again the Nestjs application.

npm start

Open the browser and go to [http://localhost:3000](http://localhost:3000`) and you should see this application.

That it's, the complete step by step tutorial of building a web app using Nestjs, Fastify, MongoDB and Angular 8. You can find the full source code in our GitHub.

angular mongodb node-js web-development

Bootstrap 5 Complete Course with Examples

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

Building a simple Applications with Vue 3

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

Hire Node.JS Developers | Skenix Infotech

We are providing robust Node.JS Development Services with expert Node.js Developers. Get affordable Node.JS Web Development services from Skenix Infotech.

How to Hire Node.js Developers And How Much Does It Cost?

A Guide to Hire Node.js Developers who can help you create fast and efficient web applications. Also, know how much does it cost to hire Node.js Developers.

Top Node.js Development Companies and Expert NodeJS Developers

A thoroughly researched list of top NodeJS development companies with ratings & reviews to help hire the best Node.JS developers who provide development services and solutions across the world. List of Leading Node.js development Service Providers...

Web Development Services

As one of the best Web Application Development Company, it provides a fine quality mobile app development service at an affordable price. Especially, it encourage start-ups that have unique ideas, by offering a more competitive price

Develop this one fundamental skill if you want to become a successful developer

Throughout my career, a multitude of people have asked me&nbsp;<em>what does it take to become a successful developer?</em>