Getting started with Angular, Cosmos DB and Web API

In this article, we will create an Angular 6 application with Web API 2.0. We will see all four CRUD operations and save the data to Cosmos DB. For testing purpose, we can use Cosmos DB emulator and save the data to the local system. After successfully completing testing, we can create a Cosmos DB service in Azure also.

For those who are new to Cosmos DB,
 Azure Cosmos DB is Microsoft’s globally distributed, multi-model database. With the click of a button, Azure Cosmos DB enables you to elastically and independently scale throughput and storage across any number of Azure’s geographic regions. It offers throughput, latency, availability, and consistency guarantees with comprehensive service level agreements (SLAs), something no other database service can offer. Currently, there are five different types of APIs are supported by Cosmos DB as given below.

  • SQL
  • MongoDB
  • Graph
  • Table
  • Cassandra

Web API Service creation

Create a new Web API 2.0 project 
WebAPI4AngularCosmosDB using Visual Studio.

I am using ASP.NET 4.5 Templates for it.

In the next step, we are going to install “Microsoft.Azure.DocumentDBNuGet” package

We can create a “DocumentDBRepository” class now. This is the core repository for Cosmos DB CRUD operations

namespace WebAPI4AngularCosmosDB  
{  
    using System;  
    using System.Collections.Generic;  
    using System.Configuration;  
    using System.Linq;  
    using System.Linq.Expressions;  
    using System.Threading.Tasks;  
    using Microsoft.Azure.Documents;  
    using Microsoft.Azure.Documents.Client;  
    using Microsoft.Azure.Documents.Linq;  
    public static class DocumentDBRepository<T> where T : class  
    {  
        private static readonly string DatabaseId = ConfigurationManager.AppSettings["database"];  
        private static readonly string CollectionId = ConfigurationManager.AppSettings["collection"];  
        private static DocumentClient client;  
  
        public static async Task<T> GetItemAsync(string id)  
        {  
            try  
            {  
                Document document = await client.ReadDocumentAsync(UriFactory.CreateDocumentUri(DatabaseId, CollectionId, id));  
                return (T)(dynamic)document;  
            }  
            catch (DocumentClientException e)  
            {  
                if (e.StatusCode == System.Net.HttpStatusCode.NotFound)  
                {  
                    return null;  
                }  
                else  
                {  
                    throw;  
                }  
            }  
        }  
  
        public static async Task<IEnumerable<T>> GetItemsAsync()  
        {  
            IDocumentQuery<T> query = client.CreateDocumentQuery<T>(  
                UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId),  
                new FeedOptions { MaxItemCount = -1 })  
                .AsDocumentQuery();  
  
            List<T> results = new List<T>();  
            while (query.HasMoreResults)  
            {  
                results.AddRange(await query.ExecuteNextAsync<T>());  
            }  
  
            return results;  
        }  
  
        public static async Task<IEnumerable<T>> GetItemsAsync(Expression<Func<T, bool>> predicate)  
        {  
            IDocumentQuery<T> query = client.CreateDocumentQuery<T>(  
                UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId),  
                new FeedOptions { MaxItemCount = -1 })  
                .Where(predicate)  
                .AsDocumentQuery();  
  
            List<T> results = new List<T>();  
            while (query.HasMoreResults)  
            {  
                results.AddRange(await query.ExecuteNextAsync<T>());  
            }  
  
            return results;  
        }  
  
        public static async Task<T> GetSingleItemAsync(Expression<Func<T, bool>> predicate)  
        {  
            IDocumentQuery<T> query = client.CreateDocumentQuery<T>(  
                UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId),  
                new FeedOptions { MaxItemCount = -1 })  
                .Where(predicate)  
                .AsDocumentQuery();  
            List<T> results = new List<T>();  
            results.AddRange(await query.ExecuteNextAsync<T>());  
            return results.SingleOrDefault();  
        }  
  
        public static async Task<Document> CreateItemAsync(T item)  
        {  
            return await client.CreateDocumentAsync(UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId), item);  
        }  
  
        public static async Task<Document> UpdateItemAsync(string id, T item)  
        {  
            return await client.ReplaceDocumentAsync(UriFactory.CreateDocumentUri(DatabaseId, CollectionId, id), item);  
        }  
  
        public static async Task DeleteItemAsync(string id)  
        {  
            await client.DeleteDocumentAsync(UriFactory.CreateDocumentUri(DatabaseId, CollectionId, id));  
        }  
  
        public static void Initialize()  
        {  
            client = new DocumentClient(new Uri(ConfigurationManager.AppSettings["endpoint"]), ConfigurationManager.AppSettings["authKey"]);  
            CreateDatabaseIfNotExistsAsync().Wait();  
            CreateCollectionIfNotExistsAsync().Wait();  
        }  
  
        private static async Task CreateDatabaseIfNotExistsAsync()  
        {  
            try  
            {  
                await client.ReadDatabaseAsync(UriFactory.CreateDatabaseUri(DatabaseId));  
            }  
            catch (DocumentClientException e)  
            {  
                if (e.StatusCode == System.Net.HttpStatusCode.NotFound)  
                {  
                    await client.CreateDatabaseAsync(new Database { Id = DatabaseId });  
                }  
                else  
                {  
                    throw;  
                }  
            }  
        }  
  
        private static async Task CreateCollectionIfNotExistsAsync()  
        {  
            try  
            {  
                await client.ReadDocumentCollectionAsync(UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId));  
            }  
            catch (DocumentClientException e)  
            {  
                if (e.StatusCode == System.Net.HttpStatusCode.NotFound)  
                {  
                    await client.CreateDocumentCollectionAsync(  
                        UriFactory.CreateDatabaseUri(DatabaseId),  
                        new DocumentCollection { Id = CollectionId },  
                        new RequestOptions { OfferThroughput = 1000 });  
                }  
                else  
                {  
                    throw;  
                }  
            }  
        }  
    }  
}  




This is a static and generic class. Inside this class, we have an “Initialize” method and it will be invoked from the “Application_Start” method inside “Global.asax” class.

When the application starts, “DocumentDBRepository<Hero>.Initialize()” will be called and it will create one Cosmos DB database and collection if it does not exist. Please note that in the first run, there was no database and collection in our Cosmos DB.

Cosmos DB
 endpoint, key, database and collection name must be stored in the Web.config file.

<configuration>  
  <appSettings>  
    <add key="webpages:Version" value="3.0.0.0" />  
    <add key="webpages:Enabled" value="false" />  
    <add key="ClientValidationEnabled" value="true" />  
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />  
    <add key="endpoint" value="https://localhost:8081" />  
    <add key="authKey" value="C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==" />  
    <add key="database" value="AngularHeroDB" />  
    <add key="collection" value="MyCollection1" />  
  </appSettings>  
  ...............

Please note that in this application we are using Cosmos DB local emulator instead of real Azure Cosmos DB service. This is for testing purposes. Once our testing is over, we will change this configuration with real Cosmos DB configurations.

You can get the Cosmos DB local emulator from below URL.

Download the emulator and install it on your local system. After successful installation, you can run the emulator and it will show as a service in your system tray.

If you click the Open Data Explorer, a local Cosmos DB emulator will be opened in your browser.
In the Explorer button, you can see currently there is no database available in our emulator.
Now, we are going to create a model Hero.cs in our Web API project inside the Models folder.
namespace WebAPI4AngularCosmosDB.Models  
{  
    using Newtonsoft.Json;  
    public class Hero  
    {  
        [JsonProperty(PropertyName = "id")]  
        public string Id { get; set; }  
        [JsonProperty(PropertyName = "uid")]  
        public string UId { get; set; }  
        [JsonProperty(PropertyName = "name")]  
        public string Name { get; set; }  
        [JsonProperty(PropertyName = "saying")]  
        public string Saying { get; set; }  
    }  
}

Please note we used JsonProperty attribute in this class so that we can convert our csharpproperty to json property easily.

Now we are going to invoke DocumentDBRepositoryclass fromApplication_Start method inside Global.asax through dependency injection.

Global.asax

namespace WebAPI4AngularCosmosDB  
{  
    using System.Web.Http;  
    using System.Web.Mvc;  
    using System.Web.Optimization;  
    using System.Web.Routing;  
    using WebAPI4AngularCosmosDB.Models;  
  
    public class WebApiApplication : System.Web.HttpApplication  
    {  
        protected void Application_Start()  
        {  
            AreaRegistration.RegisterAllAreas();  
            GlobalConfiguration.Configure(WebApiConfig.Register);  
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);  
            RouteConfig.RegisterRoutes(RouteTable.Routes);  
            BundleConfig.RegisterBundles(BundleTable.Bundles);  
  
            DocumentDBRepository<Hero>.Initialize();  
        }  
    }  
}

Please build the application and run it with your local IIS server.

DocumentDBRepository<Hero>.Initialize() will execute the Initialize method and the below two asynchronized methods also will be executed.

CreateDatabaseIfNotExistsAsync().Wait();
CreateCollectionIfNotExistsAsync().Wait();

If you check the Cosmos DB emulator data explorer, you can see that our new database and collection is created successfully.

In our Web.config configurations, we give database as AngularHeroDB and collection as MyCollection1.

We can create our “HeroController.cs” API controller and create all four CRUD methods. All these methods are very simple. Through these methods we can create, edit, update and delete the hero which we use in our Angular application.

HeroController.cs

namespace WebAPI4AngularCosmosDB.Controllers  
{  
    using WebAPI4AngularCosmosDB.Models;  
    using System;  
    using System.Collections.Generic;  
    using System.Threading.Tasks;  
    using System.Web.Http;  
  
    [RoutePrefix("api/Hero")]  
    public class HeroController : ApiController  
    {  
  
        [HttpGet]  
        public async Task<IEnumerable<Hero>> GetAsync()  
        {  
  
            IEnumerable<Hero> value = await DocumentDBRepository<Hero>.GetItemsAsync();  
            return value;  
        }  
  
        [HttpPost]  
        public async Task<Hero> CreateAsync([FromBody] Hero hero)  
        {  
            if (ModelState.IsValid)  
            {  
                await DocumentDBRepository<Hero>.CreateItemAsync(hero);  
                return hero;  
            }  
            return null;  
        }  
        public async Task<string> Delete(string uid)  
        {  
            try  
            {  
                Hero item = await DocumentDBRepository<Hero>.GetSingleItemAsync(d => d.UId == uid);  
                if (item == null)  
                {  
                    return "Failed";  
                }  
                await DocumentDBRepository<Hero>.DeleteItemAsync(item.Id);  
                return "Success";  
            }  
            catch (Exception ex)  
            {  
                return ex.ToString();  
            }  
        }  
        public async Task<Hero> Put(string uid, [FromBody] Hero hero)  
        {  
            try  
            {  
                if (ModelState.IsValid)  
                {  
                    Hero item = await DocumentDBRepository<Hero>.GetSingleItemAsync(d => d.UId == uid);  
                    if (item == null)  
                    {  
                        return null;  
                    }  
                    hero.Id = item.Id;  
                    await DocumentDBRepository<Hero>.UpdateItemAsync(item.Id, hero);  
                    return hero;  
                }  
                return null; ;  
            }  
            catch (Exception ex)  
            {  
                return null;  
            }  
  
        }  
  
  
    }  
}

If needed you can check our API methods from POSTMAN or any other REST client.

Angular Application creation.

Create a new Angular application using Angular CLI

ng new Angular4WebAPICosmosDB

It will take some time to create a new project and after successful creation open the application in any code editor. I am using Visual Studio code as IDE.

Please note that in this Angular application, we are using styles.scss file instead of default style.css file. You must change the build settings for styles in the angular.json file.

style.scss

* {  
    font-family: Arial;  
  }  
  h2 {  
    color: #444;  
    font-weight: lighter;  
  }  
  body {  
    margin: 2em;  
  }  
    
  body,  
  input[text],  
  button {  
    color: #888;  
    // font-family: Cambria, Georgia;  
  }  
  button {  
    font-size: 14px;  
    font-family: Arial;  
    background-color: #eee;  
    border: none;  
    padding: 5px 10px;  
    border-radius: 4px;  
    cursor: pointer;  
    cursor: hand;  
    &:hover {  
      background-color: #cfd8dc;  
    }  
    &.delete-button {  
      float: right;  
      background-color: gray !important;  
      background-color: rgb(216, 59, 1) !important;  
      color: white;  
      padding: 4px;  
      position: relative;  
      font-size: 12px;  
    }  
  }  
  div {  
    margin: .1em;  
  }  
    
  .selected {  
    background-color: #cfd8dc !important;  
    background-color: rgb(0, 120, 215) !important;  
    color: white;  
  }  
    
  .heroes {  
    float: left;  
    margin: 0 0 2em 0;  
    list-style-type: none;  
    padding: 0;  
    li {  
      cursor: pointer;  
      position: relative;  
      left: 0;  
      background-color: #eee;  
      margin: .5em;  
      padding: .5em;  
      height: 3.0em;  
      border-radius: 4px;  
      width: 17em;  
      &:hover {  
        color: #607d8b;  
        color: rgb(0, 120, 215);  
        background-color: #ddd;  
        left: .1em;  
      }  
      &.selected:hover {  
        /*background-color: #BBD8DC !important;*/  
        color: white;  
      }  
    }  
    .text {  
      position: relative;  
      top: -3px;  
    }  
    .saying {  
      margin: 5px 0;  
    }  
    .name {  
      font-weight: bold;  
    }  
    .badge {  
      /* display: inline-block; */  
      float: left;  
      font-size: small;  
      color: white;  
      padding: 0.7em 0.7em 0 0.5em;  
      background-color: #607d8b;  
      background-color: rgb(0, 120, 215);  
      background-color:rgb(134, 183, 221);  
      line-height: 1em;  
      position: relative;  
      left: -1px;  
      top: -4px;  
      height: 3.0em;  
      margin-right: .8em;  
      border-radius: 4px 0 0 4px;  
      width: 1.2em;  
    }  
  }  
    
  .header-bar {  
    background-color: rgb(0, 120, 215);  
    height: 4px;  
    margin-top: 10px;  
    margin-bottom: 10px;  
  }  
    
  label {  
    display: inline-block;  
    width: 4em;  
    margin: .5em 0;  
    color: #888;  
    &.value {  
      margin-left: 10px;  
      font-size: 14px;  
    }  
  }  
    
  input {  
    height: 2em;  
    font-size: 1em;  
    padding-left: .4em;  
    &::placeholder {  
        color: lightgray;  
        font-weight: normal;  
        font-size: 12px;  
        letter-spacing: 3px;  
    }  
  }  
    
  .editarea {  
    float: left;  
    input {  
      margin: 4px;  
      height: 20px;  
      color: rgb(0, 120, 215);  
    }  
    button {  
      margin: 8px;  
    }  
    .editfields {  
      margin-left: 12px;  
    }  
  }

Please modify the existingapp.component.ts file with the below changes.

app.component.ts
import { Component } from '@angular/core';  
  
@Component({  
  selector: 'app-root',  
  template: `  
    <h1>  
      Angular Heroes with Cosmos DB  
    </h1>  
    <div class="header-bar"></div>  
    <app-heroes></app-heroes>  
  `  
})  
export class AppComponent {}

Please add the below model file hero.ts. This is the model file for our type hero,

hero.ts
export class Hero {  
    uid: string;  
    name: string;  
    saying: string;  
}

Please addhero.service.ts

This service is used for handling the CRUD operations.

hero.service.ts
import { Injectable } from '@angular/core';  
import { HttpClient } from '@angular/common/http';  
  
import { Hero } from './hero';  
  
const api = 'http://localhost:1947/api';  
  
@Injectable()  
export class HeroService {  
  constructor(private http: HttpClient) { }  
  
  getHeroes() {  
    return this.http.get<Array<Hero>>(`${api}/hero`);  
  }  
  
  deleteHero(hero: Hero) {  
    return this.http.delete(`${api}/hero?uid=${hero.uid}`);  
  }  
  
  addHero(hero: Hero) {  
    return this.http.post<Hero>(`${api}/hero/`, hero);  
  }  
  
  updateHero(hero: Hero) {  
    return this.http.put<Hero>(`${api}/hero?uid=${hero.uid}`, hero);  
  }  
}

Add heroes.component.ts

This is the component file which controls the entire view,

heroes.component.ts
import { Component, OnInit } from '@angular/core';  
  
import { Hero } from './hero';  
import { HeroService } from './hero.service';  
  
@Component({  
  selector: 'app-heroes',  
  templateUrl: './heroes.component.html'  
})  
export class HeroesComponent implements OnInit {  
  addingHero = false;  
  deleteButtonSelected = false;  
  heroes: any = [];  
  selectedHero: Hero;  
  
  constructor(private heroService: HeroService) { }  
  
  ngOnInit() {  
    this.getHeroes();  
  }  
  
  cancel() {  
    this.addingHero = false;  
    this.selectedHero = null;  
  }  
  
  deleteHero(hero: Hero) {  
    this.deleteButtonSelected = true;  
    let value: boolean;  
    value = confirm("Are you sure want to delete this hero?");  
    if (value != true) {  
      return;  
    }  
    this.heroService.deleteHero(hero).subscribe(res => {  
      this.heroes = this.heroes.filter(h => h !== hero);  
      if (this.selectedHero === hero) {  
        this.selectedHero = null;  
      }  
    });  
  }  
  
  getHeroes() {  
    return this.heroService.getHeroes().subscribe(heroes => {  
      this.heroes = heroes;  
    });  
  }  
  
  enableAddMode() {  
    this.addingHero = true;  
    this.selectedHero = new Hero();  
  }  
  
  onSelect(hero: Hero) {  
    if (this.deleteButtonSelected == false) {  
      this.addingHero = false;  
      this.selectedHero = hero;  
    }  
    this.deleteButtonSelected = false;  
  }  
  
  save() {  
    if (this.addingHero) {  
      this.heroService.addHero(this.selectedHero).subscribe(hero => {  
        this.addingHero = false;  
        this.selectedHero = null;  
        this.heroes.push(hero);  
      });  
    } else {  
      this.heroService.updateHero(this.selectedHero).subscribe(hero => {  
        this.addingHero = false;  
        this.selectedHero = null;  
      });  
    }  
  }  
}

Now we can add our view, heroes.component.html

heroes.component.html
<div>  
    <ul class="heroes">  
        <li *ngFor="let hero of heroes" (click)="onSelect(hero)" [class.selected]="hero === selectedHero">  
            <button class="delete-button" (click)="deleteHero(hero)">Delete</button>  
            <div class="hero-element">  
                <div class="badge">{{hero.uid}}</div>  
                <div class="name">{{hero.name}}</div>  
                <div class="saying">{{hero.saying}}</div>  
            </div>  
        </li>  
    </ul>  
    <div class="editarea">  
        <button (click)="enableAddMode()">Add New Hero</button>  
        <div *ngIf="selectedHero">  
            <div class="editfields">  
                <div>  
                    <label>id: </label>  
                    <input [(ngModel)]="selectedHero.uid" placeholder="id" *ngIf="addingHero" />  
                    <label *ngIf="!addingHero" class="value">{{selectedHero.uid}}</label>  
                </div>  
                <div>  
                    <label>name: </label>  
                    <input [(ngModel)]="selectedHero.name" placeholder="name" />  
                </div>  
                <div>  
                    <label>saying: </label>  
                    <input [(ngModel)]="selectedHero.saying" placeholder="saying" />  
                </div>  
            </div>  
            <button (click)="cancel()">Cancel</button>  
            <button (click)="save()">Save</button>  
        </div>  
    </div>  
</div>

Finally add new file references to app.module.ts file.

app.module.ts
import { BrowserModule } from '@angular/platform-browser';  
import { NgModule } from '@angular/core';  
import { FormsModule } from '@angular/forms';  
import { HttpClientModule } from '@angular/common/http';  
  
import { AppComponent } from './app.component';  
import { HeroService } from './hero.service';  
import { HeroesComponent } from './heroes.component';  
  
@NgModule({  
  declarations: [  
    AppComponent,  
    HeroesComponent  
  ],  
  imports: [  
    BrowserModule,  
    FormsModule,  
    HttpClientModule  
  ],  
  providers: [HeroService],  
  bootstrap: [AppComponent]  
})  
export class AppModule { }

Before running our Angular application, we need to enable CORS in our Web API to allow requests from Angular application. For that, install “Microsoft.AspNet.WebApi.Cors” using NuGet

Now add the below code inside the Register method in WebApiConfig.cs file.

namespace WebAPI4AngularCosmosDB  
{  
    using System.Web.Http;  
    using System.Web.Http.Cors;  
  
    public static class WebApiConfig  
    {  
        public static void Register(HttpConfiguration config)  
        {  
            config.MapHttpAttributeRoutes();  
  
            EnableCorsAttribute cors = new EnableCorsAttribute("*", "*", "*");  
            config.EnableCors(cors);  
  
            config.Routes.MapHttpRoute(  
                name: "DefaultApi",  
                routeTemplate: "api/{controller}/{id}",  
                defaults: new { id = RouteParameter.Optional }  
            );  
        }  
    }  
}

Run the Web API and Angular application.

Our Angular application will be loaded and shown as below.

You can click the add a new hero button and add some details.

If you check the data explorer in Cosmos DB emulator, you can see one document inside our “MyCollection1″ collection as below.

You can edit and delete heroes using our Angular application.

We have used our local Cosmos DB emulator for testing purpose. You can even reset the existing data in emulator using Reset Data button of emulator.

Please select SQL as API.

Please create the service and after successful creation, you can view the keys in the Keys pane.

Copy the new URI and primary key and paste to our Web.config instead of old keys for local Cosmos DB.

Run the Web API application again and after some time, our new Azure Cosmos database and collection will be created, and you can check the data in Data Explorer pane.

If you refresh our already running Angular application, you can notice that no Hero data will be displayed because we have changed the connection from local Cosmos DB to Azure.

You can add some data again and save it. Please re-open the Data Explorer in Azure and notice that our new data will be shown in the explorer as given below.

Happy coding with Angular, Cosmos DB and local emulator too!!!

Web API project for this application can be downloaded from 
Github (WebAPI2)

Angular project for this application can be downloaded from 
Github (Angular6)

Please follow and like me:

1 thought on “Getting started with Angular, Cosmos DB and Web API

Leave a Reply

Your email address will not be published. Required fields are marked *