Azure Functions Cosmos DB Access - microsoft-campus-community/workshop-shopping-list-bot GitHub Wiki

Prerequisite: This page assumes that you have this repository forked and the functions folder open in your IDE. Learn more about the Basics of Azure Functions in the previous page.

This page explains how the Azure Functions access the Cosmos DB. This would work with any Mongo DB. Open the functions/services/cosmosDBService.ts file in your IDE. Each time a Function needs to access the database, it will create an object of the CosmosDBService class with an interface to the database. Each shopping list is uniquely identified by an id named shoppingListID. Because every request to a Function will only require work on one shopping list, the Function passes the shoppingListID to the constructor of CosmosDBService.

Creating MongoClient

In the constructor of CosmosDBService, a MongoClient object is created. The MongoClient is part of the NPM mongodb package. It is passed the URL to the database. This URL is configured through an environment variable with the name SHOPPING_LIST_COSMOSDB. You can then use the MongoClient object for an operation on the shopping list identified by shoppingListID.

Every time the CosmosDBService wants to access the database, it must establish a connection through the MongoClient object, execute the operations it wants and close the connection. Opening the connection is done in the connectAndGetCollection() function.

Add an Item to the Shopping List

The addItem function takes an Item object and should insert it into the database. Before inserting the Item, it should determine the correct position for the Item in the shopping list. This function to add an item is not yet implemented. It is your task to complete it to learn how to operate on a Mongo DB.

  1. First, the addItem function should check the input and throw an error if it receives an invalid input. The item may not be undefined, and item.itemName may not be undefined, nor an empty string.
if (!item || !item.itemName || item.itemName === '') {
            throw new Error('Illegal value for item');
}
  1. You need to find out the position in the shopping list for the item. A new item should be the last item in the shopping list. To determine its position, you need to know the number of items currently in the shopping list. When connecting to the database with our helper function connectAndGetCollection(), you get all the items for all shopping lists. With the mongodb API, you can get only the items with the shoppingListID you want to add the item to. Then you can count how many items fulfill this condition and calculate the position for the new item as follows.
const collection = await this.connectAndGetCollection();
            const positionInShoppingList: number = await collection.find({ shoppingListID: this.shoppingListID }).count() + 1;
  1. Now that you know at what position in the shopping list you should add the item, you can insert it into the database using the insertOne function of the mongodb API. The object to insert is the item without the item.id so that the Mongo DB will automatically generate an id. The object to insert contains the id of the shopping list; this new item belongs to.
return (await collection.insertOne({
        shoppingListID: this.shoppingListID,
        item: new ItemDb(item.itemName, item.marked, positionInShoppingList, item.unit)
})).ops[0].item;
  1. Lastly, you always want to close the database's connection, even if the operation fails. So you wrap the above code in a try-finally statement, and in the finally part, you close the MongoClient connection so that the entire code for the addItem function looks like the following.
/**
 * Adds a new item to the shopping list.
 *
 * Retrieves the number of items in the shopping list of the {@link shoppingListID}.
 * Sets the increases the number by one and sets it as a position in the shopping list for the item to be added.
 *
 * Precondition: Item and item's name must not be undefined, and item's name must not be an empty string.
 * 
 * Postcondition: The given item is added to the shopping list with the last position in the shopping list, and the added item is returned.
 *      An error is thrown if a DB API call failed.
 * @param item the item to be added.
 * @returns the item which was added.
 */
public async addItem(item: Item): Promise<Item> {
    if (!item || !item.itemName || item.itemName === '') {
        throw new Error('Illegal value for item');
    }
    try {
        const collection = await this.connectAndGetCollection();
        const positionInShoppingList: number = await collection.find({ shoppingListID: this.shoppingListID }).count() + 1;
        return (await collection.insertOne({
            shoppingListID: this.shoppingListID,
            item: new ItemDb(item.itemName, item.marked, positionInShoppingList, item.unit)
        })).ops[0].item;
    } finally {
        await this.client.close();
    }
}

Now you have an example of how to work with the Mongo DB API. If you want to learn how other operations work, please look at the remaining code in the CosmosDBService. This class implements all the access methods to the Mongo DB. However, when creating the MongoClient, it passes the connection string to a Mongo DB from the environment variable SHOPPING_LIST_COSMOSDB. So to access the database, some configuration is needed. You will learn how to configure this environment variable in the next part.

⚠️ **GitHub.com Fallback** ⚠️