In todays's tutorial, we are going to build a full-stack web application using Nest.js and Angular. In here, we'll be building a simple contacts management web application in an Angular and Material Design interface retrieved from a Nest.js RESTful backend.

Prerequisites

  • Angular installed on your pc
  • To proceed with this tutorial you need to Angular installed in your local pc. to check, if Angular is installed or not run below command in your terminal:

    1
    ng version

    To install Angular in your local pc, run below coomand in your terminal:

    1
    npm install -g @angular/cli

  • Nest.js installed on your pc
  • To install Nest.js on your local pc, run below command in your terminal:
    1
    npm install -g @nestjs/cli

    Setup Working Directory

    Now we need to setup our working directory. To setup directory, Head back to your terminal and navigate to your working directory then create a folder your project:
    1
    2
    cd ~/codingtricks
    mkdir nest-angular-app

    Now we need to navigate to our created project and generate the backend and frontend projects using the following commands:

    1
    2
    3
    cd nest-angular-app
    ng new frontend
    nest new backend

    Note:

    Then the Angular CLI will prompt you if you Would you like to add Angular routing? (y/N) Enter y and Which stylesheet format would you like to use? Choose CSS and hit Enter.

    The Nest CLI will ask you for a bunch of information about your project and CLI will create the necessary files and then prompt you for which package manager to use to install the dependencies. Choose npm.

    Next, navigate to your frontend project and serve it using a live-reload development server using the following commands:

    1
    2
    cd frontend
    ng serve

    Open a new terminal and navigate to the backend project then run the development server using the following commands:

    1
    2
    cd backend
    npm run start:dev

    Set up Database and TypeORM

    ext, we nned to create a Databse for our project. To create Database access to your php my admin and create database with your prefered name. In my case my Database name is nestng.

    Previously I mentioned that we are going to use of TypeORM which is the most mature Object Relational Mapper (ORM) available in TypeScript.

    Navigate to your backend project and run the following commands to install the required dependencies:

    1
    npm install --save @nestjs/typeorm typeorm mysql

    Next, you need to open the src/app.module.ts file and import the TypeOrmModule in ApplicationModule:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    import { Module } from '@nestjs/common';
    import { TypeOrmModule } from '@nestjs/typeorm';

    import { AppController } from './app.controller';
    import { AppService } from './app.service';

    @Module({
    imports: [
    TypeOrmModule.forRoot({
    type: 'mysql',
    database: 'nestng',
    username: 'root',
    password: 'YOUR_DATABASE_PASSWORD',
    entities: [__dirname + '/**/*.entity{.ts,.js}'],
    synchronize: true,
    })
    ],
    controllers: [AppController],
    providers: [AppService],
    })
    export class AppModule { }

    Note:

    Make sure to replace YOUR_DATABASE_PASSWORD with your own MySQL database password.

    In the configuration object we specify the following options:

  • mysql database as the type which tells TypeORM to connect to a MySQL database,
  • nestng database we previously created
  • entities files contain our ORM entities that will be mapped to SQL tables by TypeORM.
  • synchronize option (which takes true or false). If true, It tells TypeORM to automatically sync the database tables with the entities each time you run the app. This is helpful in development but not recommended in production.

  • Now, we have configured TypeORM in our project, we’ll be able to inject the Connection and EntityManager services in your Nest.js services and controllers to work with the database

    Creating a Contact Entity

    Next, we need to create a Contact entity that will be mapped to a contact table in the MySQL database that will hold information about contacts in our application.

    Create a src/entities/contact.entity.ts file and add the following code:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

    @Entity()

    export class Contact {

    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    name: string;

    @Column()
    title: string;

    @Column()
    email: string;

    @Column()
    phone: string;

    @Column()
    address: string;

    @Column()
    city: string;
    }

    Next, we need to import that Contact entity and include it in the imports array of the root module using the forFeature() method. Open the src/app.module.ts file and add the following changes:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // ...
    import { Contact } from 'entities/contact.entity';

    @Module({
    imports: [
    /* ... */
    TypeOrmModule.forFeature([Contact]),
    ],
    controllers: [AppController],
    providers: [AppService],
    })

    export class AppModule { }

    Once you save your file, the database table that corresponds to the Contact entity will be created in your database.

    Creating Contact Service

    Next, let's create a Nest service that contains the code for working with the contact table.

    Head back to your terminal and run the following command to generate a service:

    1
    nest generate service contact

    Next, open the src/contact.service.ts file and add the following imports:

    1
    2
    3
    import { Repository } from  'typeorm';
    import { Repository, UpdateResult, DeleteResult } from 'typeorm';
    import { Contact } from 'entities/contact.entity';

    Next, inject the Contact repository via the constructor:

    1
    2
    3
    4
    5
    6
    7
    8
    @Injectable()
    export class ContactService {
    constructor(
    @InjectRepository(Contact)
    private contactRepository: Repository<Contact>
    )
    { }
    }

    Next, add the CRUD methods:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

    async create(contact: Contact): Promise<Contact> {
    return await this.contactRepository.save(contact);
    }

    async readAll(): Promise<Contact[]> {
    return await this.contactRepository.find();
    }

    async update(contact: Contact): Promise<UpdateResult> {

    return await this.contactRepository.update(contact.id,contact);
    }

    async delete(id): Promise<DeleteResult> {
    return await this.contactRepository.delete(id);
    }

    Building the REST API

    Now we need to create the REST endpoints that our Angular frontend will be calling to retrieve and create data on the server. Head back to your terminal and run the following command to generate a controller:
    1
    nest generate controller contacts

    Next, open the src/contacts/contacts.controller.ts file and import then inject ContactService

    1
    2
    3
    4
    5
    6
    7
    8
    9
    import { Controller } from '@nestjs/common';
    import { ContactService } from 'contact.service';

    @Controller('contacts')
    export class ContactsController {

    constructor(private contactService: ContactService){
    }
    }

    You need to import the Get, Post,Put, Delete, Body and Param symbols and also the Contact entity:

    1
    2
    import { Controller, Get, Post,Put, Delete, Body, Param } from  '@nestjs/common';
    import { Contact } from 'entities/contact.entity';

    Finally, add the REST endpoints:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    @Get()
    read(): Promise<Contact[]> {
    return this.contactService.readAll();
    }

    @Post('create')
    async create(@Body() contact: Contact): Promise<any> {
    return this.contactService.create(contact);
    }

    @Put(':id/update')
    async update(@Param('id') id, @Body() contact: Contact): Promise<any> {
    contact.id = Number(id);
    return this.contactService.update(contact);
    }

    @Delete(':id/delete')
    async delete(@Param('id') id): Promise<any> {
    return this.contactService.delete(id);
    }

    That’s it! Our REST API is ready.

    Enabling CORS

    Since we'll be communicating with our REST API from an Angular frontend running on another domain we need to enable CORS. Open the src/main.ts file and call the enableCors() method:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    import { NestFactory } from '@nestjs/core';
    import { AppModule } from './app.module';

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

    Implementing the Angular Interface

    Now we need to add Angular Material for our Angular project. Head back to a new terminal and run the following command to set up Angular Material:
    1
    ng add @angular/material

    After that, you need to import the Angular Material components that you want to use in your project. Open the src/app/app.module.ts file:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    // [...]

    import { MatInputModule,
    MatButtonModule,
    MatCardModule,
    MatFormFieldModule,
    MatTableModule
    } from '@angular/material';

    @NgModule({
    declarations: [
    AppComponent,
    ContactComponent
    ],
    imports: [
    // [...]
    MatTableModule,
    MatCardModule,
    MatInputModule,
    MatFormFieldModule,
    MatButtonModule
    ],
    providers: [],
    bootstrap: [AppComponent]
    })

    export class AppModule { }

    Setting up HttpClient and Forms

    We'll be using HttpClient and a template-based form in our application so we need to import their modules. Open the src/app/app.module.ts file and update it accordingly:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // [...]
    import { HttpClientModule } from '@angular/common/http';
    import { FormsModule } from '@angular/forms';

    @NgModule({
    declarations: [
    AppComponent
    ],
    imports: [
    // [...]
    HttpClientModule,
    FormsModule
    ],
    providers: [],
    bootstrap: [AppComponent]
    })

    export class AppModule { }

    Creating a Contact Model

    Next, let's create a Contact model. In your terminal, run the following command:
    1
    ng generate class contact

    Open the src/app/contact.ts file and update it accodringly:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    export class Contact {
    id: number;
    name: string;
    title: string;
    email: string;
    phone: string;
    address: string;
    city: string;
    }

    Creating an Angular Service

    Next, let's create a service that contains the code for interfacing with the REST API. In your terminal, run the following command:
    1
    ng generate service api

    Next, open the src/app/api.service.ts file and start by importing and injecting HttpClient:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import { Injectable } from '@angular/core';
    import { HttpClient } from '@angular/common/http';
    import { Contact } from './contact';

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

    constructor(private httpClient: HttpClient) { }
    }

    We also imported the Contact model.

    Next, define the API_SERVER variable that contains the address of the API server:

    1
    2
    3
    4
    export class ApiService {

    API_SERVER = "http://localhost:3000";

    Next, add the CRUD operations:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    public readContacts(){
    return this.httpClient.get<Contact[]>(`${this.API_SERVER}/contacts`);
    }

    public createContact(contact: Contact){
    return this.httpClient.post<Contact>(`${this.API_SERVER}/contacts/create`, contact);
    }

    public updateContact(contact: Contact){
    return this.httpClient.put<Contact>(`${this.API_SERVER}/contacts/${contact.id}/update`, contact);
    }

    public deleteContact(id: number){
    return this.httpClient.delete(`${this.API_SERVER}/contacts/${id}/delete`);
    }

    We use the get(), post(), put() and delete() methods of HttpClient to send the GET, POST, PUT and DELETE requests to the corresponding endpoints of the API backend

    Creating the Contacts Component

    Now, let's create the component where we can display our table of contacts and a form to create contacts.

    In your terminal, run the following command:

    1
    ng generate component contact

    Next, open the src/app/app-routing.module.ts file and add a route for accessing the component:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import { NgModule } from '@angular/core';
    import { Routes, RouterModule } from '@angular/router';
    import { ContactComponent } from './contact/contact.component';

    const routes: Routes = [
    {path: "", pathMatch: "full", redirectTo: "contacts"},
    {path: "contacts", component: ContactComponent}
    ];

    // [...]

    We add a /contacts route mapped to ContactComponent and a route to redirect the empty path to the /contacts path.

    Next open the src/app/contact/contact.component.ts and add the following imports:

    1
    2
    import { ApiService } from '../api.service';
    import { Contact } from '../contact';

    Next inject ApiService and declare the following variables:

    1
    2
    3
    4
    5
    6
    export class ContactComponent implements OnInit {

    displayedColumns : string[] = ['id', 'name', 'title', 'email', 'phone', 'address', 'city', 'actions'];
    dataSource = [];
    contact = {};
    constructor(private apiService: ApiService) { }

    The displayedComumns variable holds the name of the columns that will be displayed in the Angular Material Table we’ll be adding later. The dataSource variable will contain the data of the table and the contact variable will contain the selected contact from the table.

    Next, retrieve the contacts from the server when the component is initialized:

    1
    2
    3
    4
    5
    ngOnInit() {
    this.apiService.readContacts().subscribe((result)=>{
    this.dataSource = result;
    })
    }

    We simply call the readContacts() method of ApiService in the ngOnInit() method of the component and we subscribe to the returned RxJS Observable then we assign the result to the dataSource variable.

    Next, add the selectContact() and newContact() methods:

    1
    2
    3
    4
    5
    6
    7
    selectContact(contact){
    this.contact = contact;
    }

    newContact(){
    this.contact = {};
    }

    The selectContact() method assigns the selected contact to the contact variable and the newContact() method assigns an empty object to the contact variable. These two methods will be bound to a select and new buttons that will be adding later in our UI.

    Finally, add the other CRUD methods to create, update and delete contacts:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    createContact(f){
    this.apiService.createContact(f.value).subscribe((result)=>{
    console.log(result);
    });

    }

    deleteContact(id){
    this.apiService.deleteContact(id).subscribe((result)=>{
    console.log(result);
    });
    }

    updateContact(f){
    f.value.id = this.contact['id'];
    this.apiService.updateContact(f.value).subscribe((result)=>{
    console.log(result);
    });
    }

    Creating the Template

    Open the src/app/contact/contact.component.html and start by adding a Material Design table that will display the contacts:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    <mat-card>
    <mat-card-header>
    <mat-card-title>Contacts</mat-card-title>
    </mat-card-header>
    <mat-card-content>
    <table mat-table [dataSource]="dataSource" class="mat-elevation-z8" style="width: 100%;">
    <ng-container matColumnDef="id">
    <th mat-header-cell *matHeaderCellDef>ID </th>
    <td mat-cell *matCellDef="let element"> {{element.id}} </td>
    </ng-container>
    <ng-container matColumnDef="name">
    <th mat-header-cell *matHeaderCellDef>Name </th>
    <td mat-cell *matCellDef="let element"> {{element.name}} </td>
    </ng-container>
    <ng-container matColumnDef="title">
    <th mat-header-cell *matHeaderCellDef> Title </th>
    <td mat-cell *matCellDef="let element"> {{element.title}} </td>
    </ng-container>
    <ng-container matColumnDef="email">
    <th mat-header-cell *matHeaderCellDef> Email </th>
    <td mat-cell *matCellDef="let element"> {{element.email}} </td>
    </ng-container>
    <ng-container matColumnDef="phone">
    <th mat-header-cell *matHeaderCellDef> Phone </th>
    <td mat-cell *matCellDef="let element"> {{element.phone}} </td>
    </ng-container>

    <ng-container matColumnDef="address">
    <th mat-header-cell *matHeaderCellDef> Address </th>
    <td mat-cell *matCellDef="let element"> {{element.address}} </td>
    </ng-container>

    <ng-container matColumnDef="city">
    <th mat-header-cell *matHeaderCellDef> City </th>
    <td mat-cell *matCellDef="let element"> {{element.city}} </td>
    </ng-container>

    <ng-container matColumnDef="actions">
    <th mat-header-cell *matHeaderCellDef> Actions </th>
    <td mat-cell *matCellDef="let element">
    <button mat-raised-button (click)="deleteContact(element.id)" color="secondary">Delete Contact</button>
    <button mat-raised-button (click)="selectContact(element)" color="secondary">Select Contact</button>

    </td>
    </ng-container>

    <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
    <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
    </table>

    </mat-card-content>
    </mat-card>

    Next, let’s add a form to create and update contacts just below the table:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    <mat-card>
    <mat-card-header>
    <mat-card-title>Create a Contact</mat-card-title>
    </mat-card-header>
    <mat-card-content>
    <form #f ="ngForm" class="mat-elevation-z8" style="width: 100%; padding: 5px;">

    <mat-form-field>
    <input matInput placeholder="Name" name="name" [(ngModel)] = "contact.name" required>
    </mat-form-field>
    <mat-form-field>
    <input matInput placeholder="Title" name="title" [(ngModel)] = "contact.title" required>
    </mat-form-field>

    <mat-form-field>
    <input matInput placeholder="Email" name="email" [(ngModel)] = "contact.email" required>
    </mat-form-field>
    <mat-form-field>
    <input matInput placeholder="Phone" name="phone" [(ngModel)] = "contact.phone" required>
    </mat-form-field>
    <mat-form-field>
    <textarea placeholder="Address" name="address" [(ngModel)] = "contact.address" matInput></textarea>
    </mat-form-field>
    <mat-form-field>
    <input matInput placeholder="City" name="city" [(ngModel)] = "contact.city" required>
    </mat-form-field>

    </form>

    </mat-card-content>
    <mat-card-actions>
    <button mat-raised-button *ngIf="!contact.id" (click)="createContact(f)" color="primary">Save Contact</button>
    <button mat-raised-button *ngIf="contact.id" (click)="updateContact(f)" color="primary">Update Contact</button>
    <button mat-raised-button (click)="newContact()" color="primary">New..</button>

    </mat-card-actions>
    </mat-card>

    Next, open the src/app/app.component.html and change accordingly:

    1
    2
    3
    4
    5
    6
    <div style="text-align:center">
    <h1>
    Contact Management
    </h1>
    </div>
    <router-outlet></router-outlet>

    Conclusion

    In this tutorial, we obtain how can we create built a full-stack web application with Angular and Nest.js. You can obtain complete source code for this tutorial from this GitHub repository.

    Happy Coding