How to create a CRUD application using Azure Cosmos DocumentDB?

In this post, I’ll tell you how to create a CRUD application using Azure Cosmos DocumentDB with a front-end framework of your choice. The main focus of this post is DocumentDB and the front-end framework can be of your choice like ASP.NET MVC, Angular 2, AngularJS or some other framework.

Application

The project code used in this post is located on GitHub, so feel free to use it for learning purpose. I’ll be using ASP.NET MVC framework for this post. If you are using something else, even then this post can help you to learn about DocumentDB.

Azure Cosmos DB & Setup

Azure Cosmos DB is Microsoft’s globally distributed, multi-model database service. To work with DocumentDB, an Azure account is required which is free for 30 days. If you already have an Azure Cosmos DB service account, then you can directly create a database. If not, please create this account by using the azure portal by going through New > Databases > Azure Cosmos DB.

For the new account setup, give a unique id for this resource in a new or existing resource group. For API, choose SQL (DocumentDB) as this is the focus of this post and choose a location. It will take few seconds to create this service and once the account is ready, you are all set to create  a database.

DocumentDB allow us to create a database and have collections inside it. Just to note that, a collection as a concept is not same as a table in relational database. As such there is no schema for the data that will be stored, the data can be of any shape. Create a new database called `CourseDb` and create a collection named `Courses` inside the same database. You may name these anything else as well. The same portal can also be used to view, create, edit, delete documents using Data Explorer (Preview) feature.

I’m not going to paste any ASP.NET MVC code here and it will make the post lengthy. Please look at the repository for this purpose. To keep this post simple and focus on the CRUD (create, read update, delete), I’m going to create a simple Course application that is used to manage courses, say in a school or college or university. The definition of Course type looks like following:

public class Course
{
    [JsonProperty("id")]
    public string Id { get; set; }

    [JsonProperty("title")]
    public string Title { get; set; }

    [JsonProperty("modules")]
    public string Modules { get; set; }

    [JsonProperty("completed")]
    public bool Completed { get; set; }
}

The class definition is quite simple. The properties in it are decorated with `JsonProperty` attribute from Newtonsoft.Json framework to define correct property name that will be used to map with the result that Azure will give us back when using DocumentDB.

To consume DocumentDB from Azure in your ASP.NET MVC application, a package named `Microsoft.Azure.DocumentDB` is required in the project using Nuget Package Manager. I’ve used version 1.15.0.  This package gives us a `DocumentClient` class that can be used to talk to DocumentDB in any Azure instance by just providing the URI of your Cosmos DB service and an authentication key.

Go to Settings > Keys and get the URI, Primary Key.  As such actions like create, update and delete are required for application, use read-write keys. Copy these two values in the `appSettings` of web.config file, like this:

<appSettings>
    <add key="CosmosDbUri" value="{{your URI value}}" />
    <add key="CosmosDbKey" value="{{your read-write primary key}}" />
    <add key="DatabaseId" value="CourseDb" />
    <add key="CollectionId" value="Courses" />
</appSettings>

The other two values are the name of database and collection as all of these information will be used to talk to the Azure DocumentDB instance. I’ve created a Repository that is responsible to encapsulate create, read, update, delete actions and expose some methods that can be used by the consumer i.e. controller.

Let us look at how this repository handles the initialization process in the code below. This class is using generic so that the same repository can be used for any other collection other than Courses. The Initialize method creates a `DocumentClient` class instance by using the URI and Primary Key by reading the values from `appSettings`. The instance is then stored in a private static field in this repository.

public class DocumentDbRepository<T> where T : class
{
    private static readonly string DatabaseId = ConfigurationManager.AppSettings["DatabaseId"];
    private static readonly string CollectionId = ConfigurationManager.AppSettings["CollectionId"];
    private static DocumentClient _documentClient;

    public static void Initialize()
    {
        Uri serviceEndpoint = new Uri(ConfigurationManager.AppSettings["CosmosDbUri"]);
        string authKey = ConfigurationManager.AppSettings["CosmosDbKey"];

        _documentClient = new DocumentClient(serviceEndpoint, authKey);
        CreateDatabaseIfNotExistsAsync().Wait();
        CreateCollectionIfNotExistsAsync().Wait();
    }

Next step is to ensure to check whether the database, collection that we are going to use in this application exists or not. If not, then create them so that it can be used without any runtime exceptions. The definition of these methods looks like following:

private static async Task CreateDatabaseIfNotExistsAsync()
{
    try
    {
        await _documentClient.ReadDatabaseAsync(UriFactory.CreateDatabaseUri(DatabaseId));
    }
    catch (DocumentClientException e)
    {
        if (e.StatusCode == System.Net.HttpStatusCode.NotFound)
        {
            await _documentClient.CreateDatabaseAsync(new Database { Id = DatabaseId });
        }
        else
        {
            throw;
        }
    }
}

private static async Task CreateCollectionIfNotExistsAsync()
{
    try
    {
        Uri documentCollectionLink = UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId);
        await _documentClient.ReadDocumentCollectionAsync(documentCollectionLink);
    }
    catch (DocumentClientException e)
    {
        if (e.StatusCode == System.Net.HttpStatusCode.NotFound)
        {
            await _documentClient.CreateDocumentCollectionAsync(
                UriFactory.CreateDatabaseUri(DatabaseId),
                new DocumentCollection { Id = CollectionId },
                new RequestOptions { OfferThroughput = 400 });
        }
        else
        {
            throw;
        }
    }
}

The most of the client frameworks of NoSQL databases like DocumentDB, RavenDB, etc. needs a source i.e. a valid resource URI to make the client instance so as to communicate with the source. These clients basically makes HTTP calls internally to get to the particular database, collection and query as per what was demanded by the consumer. So, in the code above, you will see reference to UriFactory.CreateDatabaseUri to build up a valid URI based on the database reference and then it is used to communication to the source.

The same `DocumentClient` also exposes many useful API endpoints that can be used to create, read, update and delete a collection or a database. Below is the code from the same custom repository that takes care of handling these actions:

public static async Task<T> GetItemAsync(string id)
{
    try
    {
        Document document = await _documentClient.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(Expression<Func<T, bool>> predicate)
{
    IDocumentQuery<T> query = _documentClient.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<Document> CreateItemAsync(T item)
{
    return await _documentClient.CreateDocumentAsync(UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId), item);
}

public static async Task<Document> UpdateItemAsync(string id, T item)
{
    return await _documentClient.ReplaceDocumentAsync(UriFactory.CreateDocumentUri(DatabaseId, CollectionId, id), item);
}

public static async Task DeleteItemAsync(string id)
{
    await _documentClient.DeleteDocumentAsync(UriFactory.CreateDocumentUri(DatabaseId, CollectionId, id));
}

This `Initialize` method of this repository class is then called from `Application_Start` method located in `Global.asax.cs` file like following:

public class MvcApplication : 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<Course>.Initialize();
    }
}

For any issues, please use my repository or comment here. I hope this post easily explains how to create a CRUD application using Azure Cosmos DocumentDB with a front-end framework of your choice. Please subscribe to my website to get more updates on similar topics.

Siddharth Pandey

Siddharth Pandey is a Software Engineer with thorough hands-on commercial experience & exposure to building enterprise applications using Agile methodologies. Siddharth specializes in building, managing on-premise, cloud based real-time standard, single page web applications (SPAs). He has successfully delivered applications in health-care, finance, insurance, e-commerce sectors for major brands in the UK. Other than programming, he also has experience of managing teams, trainer, actively contributing to the IT community by sharing his knowledge using Stack Overflow, personal website & video tutorials.

You may also like...

Advertisment ad adsense adlogger