Click here to Skip to main content
14,299,664 members

Angular 6 Application with Cosmos DB and Web API 2

Rate this:
5.00 (6 votes)
Please Sign up or sign in to vote.
5.00 (6 votes)
12 Aug 2018CPOL
We will create an Angular 6 application with Cosmos DB and Web API 2.0.

Introduction

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 purposes, we can use Cosmos DB emulator and save the data to local system. After successfully completed testing, we can create a Cosmos DB service in Azure also.

Background

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 that are supported by Cosmos DB as given below.

  • SQL
  • MongoDB
  • Graph
  • Table
  • Cassandra

Implementation

Web API Service Creation

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

Image 1

I am using ASP.NET 4.5 Templates for it.

Image 2

Next step, we are going to install Microsoft.Azure.DocumentDB NuGet package.

Image 3

We can create a DocumentDBRepository class now. This is the core repository for CosmosDB CRUD operations.

DocumentDBRepository.cs

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.

Image 4

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 the below URL:

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

Image 5

If you click the Open Data Explorer, local Cosmos DB emulator will be opened in your browser.

Image 6

In the Explorer button, you can see currently there is no database available in our emulator.

Image 7

Now we are going to create a model Hero.cs in our Web API project inside the model folder.

Hero.cs

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 that we used JsonProperty attribute in this class so that we can convert our C# property to json property easily.

Now we are going to invoke DocumentDBRepository class from Application_Start method inside Global.asaxthrough 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 with your local IIS server.

DocumentDBRepository<Hero>.Initialize() will execute 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.

Image 8

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 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 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 style.scss file instead of default style.css file. You must change the build settings for styles in angular.json file.

Image 9

Download the entire Angular 6 source code from Github and run npm install.

Before running 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.

Image 10

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:

Image 11

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

Image 12

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

Image 13

You can edit and delete heroes using our Angular application.

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

Image 14

Please select SQL as API.

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

Image 15

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.

Image 16

If you refresh our already running Angular application, you can notice that there is no Hero data that 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:

Image 17

Happy coding with Angular 6, Cosmos DB and local emulator too.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Sarathlal Saseendran
Technical Lead Orion India Systems P Ltd
India India
A passionate human being loves to learn new things always.

Full stack developer interested in .Net, Azure, Scala, Spark, Angular 6 and now Python.

https://www.linkedin.com/in/sarathlal-saseendran-28478b70

Comments and Discussions

 
GeneralMy vote of 5 Pin
Praveen Nair (NinethSense)14-Aug-18 6:16
memberPraveen Nair (NinethSense)14-Aug-18 6:16 
GeneralRe: My vote of 5 Pin
Sarathlal Saseendran14-Aug-18 9:46
memberSarathlal Saseendran14-Aug-18 9:46 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Article
Posted 12 Aug 2018

Stats

7.5K views
11 bookmarked