Cosmos DB CRUD operations using Azure Functions
Tags: azure, functions, cosmosdb, node
Published: 2020-06-06 00:22:36

A few days ago I started working with Azure Functions and Cosmos DB from scratch, since natively these types of services work very well to integrate small query services without the need to implement much code. In this tutorial we will show you how to create CRUD operations using only Azure Functions and Cosmos DB to store and query the information.

## Create a new database on Azure Cosmos DB

The first thing we need is to create a new Cosmos DB service. Cosmos DB is a service for the administration of non-SQL databases for the development of applications. It offers support for some of the most popular APIs like SQL, MongoDB and Cassandra.

### Service creation
To provision a new database we need to do the following:
  1. In the Azure main page let's a new resource.
  2. We search for **Azure Cosmos DB** and click **create**.
  3. We fill out the fields to complete the provision process
  ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/vp8y3dih2ipcmqz3jkbv.jpg)

  4. In **Networking** we mark the option **Connectivity method** as **All Networks**
  5. In **Encryption** we mark the option **Data Encryption** as **Service-managed key** so Azure can handle the keys to connect to the service
  5. We finish by clicking on **Review + Create** 

### Configure database and insert some data
Once we have the service activated we need to create the database, the collection, and insert some items. We are going to do the following:

1. Click on **+ Add Container** in the Cosmos DB instance we created.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/o442kacc0hcp72srhd1w.jpg)
2. Set a database name. We can unselect the **Provision database throughtput** option, we don't need it for this tutorial.
3. Set a container name and one partition key. The partition key is a logic form to store information in Cosmos DB. You can find more information in the [Microsoft documentation](https://docs.microsoft.com/en-us/azure/cosmos-db/partitioning-overview).
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/x3uvgwd4vzyhoakikjmk.jpg)
4. In my case I used the following names:
    - Database id: dbtodos
    - Container id: items 
    - Partition key: /all

5. Go to the Data Explorer section on the left menum to insert a new item
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/zdgx63q96zhfhg5mk7k3.jpg)

6. Click on **New Item** and we add two properties, title and completed. If we don't write an id property it's going to be generated automatically when we save the item.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/g5pa9pra7g6pgsmtgq8v.jpg)


## Azure Functions creation

We are going to start creating our functions. We are going to create different Azure Functions for each of the operations that we have to implement. Every function is going to be configured with the bindings referencing our Cosmos DB instance, and we're going to see that for the operation of removing an element from our database we'll be using the @azure/cosmos module.

### Function app creation

1. Search for **Function App** and click on *create*.
2. Configure the services as follows:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/4sndc9m5w4j1tr3w0blw.jpg)
    - Function App name: **function-crud-cosmosdb**
    - Publish: **Code**
    - Runtine stack: **Node.js**
    - Version: **12**
    - Region: **Central US**
3. For Hosting section:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/jlytsdp53308p8jelaf3.jpg)
    - Operating System: **Windows**
    - Plan type: **Consumption (Serverless)**
4. Click on **Review + Create**

### Application Settings configuration

Before starting, we have to configure some environment variables that will be used for communication between Azure Functions and Cosmos DB, this allows us to make requests to our database, This steps needs to be done only once for all out Azure functions.

1. Open your Cosmos DB service and click **Keys** on the left side menu. 
2. Copy the string from **URI**, **PRIMARY KEY** and **PRIMARY CONNECTION STRING**. We are going to need these values later.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/y39gavxyu6fyy71dxhcr.jpg)

3. In the Function app, go to **Application settings**
4. Click on **+ New application setting** to create a new setting.
5. Set a name and paste the **PRIMARY CONNECTION STRING**. In my case the setting is called **cdb-vue-todos_DOCUMENTDB**
6. Create another configuration and paste the **URI** string. (COSMOS_API_URL) and another one for the **PRIMARY KEY** (COSMOS_API_KEY) string.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/juhkz5po2s98l7xsdduu.jpg)

### Get items

1. Once the function is created, click on **Functions** and click on **+ Add**.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/5n9cffggpyxtahaauyed.jpg)
2. Choose **HTTP trigger** so the function activates on every HTTP request.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/cn5cdfgxo8xlf4voep0y.jpg)
3. Give a name to your function, mine is called **getTodos**. Authorization level is **Anonymous**.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/o6hm353zhebbn2deesma.jpg)
4. Once the function is created go to **Code + Test** to configure the bindings and code.
5. On the editor select the **function.json** file and paste the following code:
```javascript
{
  "bindings": [
    {
      "authLevel": "anonymous",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": [
        "get"
      ]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    },
    {
      "type": "cosmosDB",
      "name": "inputDocument",
      "databaseName": "dbtodos",
      "collectionName": "items",
      "connectionStringSetting": "cdb-vue-todos_DOCUMENTDB",
      "partitionKey": "/all",
      "direction": "in"
    }
  ],
  "disabled": false
}
```
**function.json** is the file where we configure the bindings for our function. It's an array of objects where every object is a binding. The last object is the binding for our Cosmos DB database. It has configured with a **`cosmosDB`** type, and a variable associated to use in our code called **`inputDocument`**. The properties databaseName, collectionName, connectionStringSetting and partitionKey must have your own values.

With the parameter direction we can say if it's an input **`in`** or an output **`out`**. For this function an input binding means we can query our database. Given that we aren't specifying any additional query the function will return all objects.

In **index.js** we implement our code to manage the function. The binding has the `inputDocument` variable, that is where the results of our query are stored. We can show the items as a response.
```javascript
module.exports = async function (context, req) {

    context.res = {
            // status: 200, /* Defaults to 200 */
            body: context.bindings.inputDocument
        };
};
```

If we test the URL to see the results we'll get the following:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/1eldx8hvwhqa01zi8tkh.jpg)

### Insert a new item

The next function has the same principle than the previous one, with only one change:

```javascript
{
  "bindings": [
    {
      "authLevel": "anonymous",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": [
        "post"
      ]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    },
    {
      "type": "cosmosDB",
      "name": "outputDocument",
      "databaseName": "dbtodos",
      "collectionName": "items",
      "createIfNotExists": false,
      "connectionStringSetting": "cdb-vue-todos_DOCUMENTDB",
      "partitionKey": "/all",
      "direction": "out"
    }
  ],
  "disabled": false
}
```

```javascript
module.exports = async function (context, req) {

    const title = req.body.title; 
    const completed = req.body.completed; 
    
    if (title && completed != null) {

        context.bindings.outputDocument = req.body;
        context.res = {
            body: {'result': 'success'}
        };
    }else{
        context.res = {
            status: 400,
            body: {'result': 'error'}
        };
    }
};
```
This function works under POST requests. It expects two parameters in the body request to insert a new item in the database. If we don't set a `title` and `completed` parameter the function returns an error as a response, otherwise we use the variable `outputDocument` to assign the `req.body` object that has the values we want to insert.

### Update an item

Repeat the steps 1 to 4 to create the **updateTodo** function. This is the binding:
```javascript
{
  "bindings": [
    {
      "authLevel": "anonymous",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": [
        "get",
        "post"
      ]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    },
    {
      "type": "cosmosDB",
      "name": "inputDocument",
      "databaseName": "dbtodos",
      "collectionName": "items",
      "connectionStringSetting": "cdb-vue-todos_DOCUMENTDB",
      "partitionKey": "/all",
      "direction": "in",
      "sqlQuery": "select * from c where c.id = {id}"
    },
    {
      "type": "cosmosDB",
      "name": "outputDocument",
      "databaseName": "dbtodos",
      "collectionName": "items",
      "createIfNotExists": false,
      "connectionStringSetting": "cdb-vue-todos_DOCUMENTDB",
      "partitionKey": "/all",
      "direction": "out"
    }
  ],
  "disabled": false
}
```

The Javascript file is as follows:
```javascript
module.exports = async function (context, req, todo) {

    const title = req.body.title;
    const completed = req.body.completed;

    context.bindings.outputDocument = todo[0];
    context.bindings.outputDocument.title = title
    context.bindings.outputDocument.completed = completed

    context.res = {
        body: {'result': 'success'}
    };
};
```
For this function we have an input and output binding. We use the input binding with a sql query to get the specific item to update, and then the output binding to change the values. 

Notice that the input binding has a sqlQuery parameter where we can explicitly a SQL query to get an item based on the id `"sqlQuery": "select * from c where c.id = {id}"`. There's a placeholder for id. When the function detects that there's an id in the http request this is going to be replaced on the placeholder. 

The output binding is used to assign the item we got from the query. The result can be `context.bindings.inputDocument` or an additional parameter in our function, in this case the variable `todo`

### Remove an item

For this function we need to do an additional implementation. Because we can't use the bindings to remove items from our database we need to use the @azure/cosmos module.

1. Go to the Function app > **Developer tools**. Click on **Go**
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/6ie79b2crqbq671p1n33.jpg)

2. Go to **Debug console > CMD** and then in the diles list to **site > wwwroot**
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/9t9vymx6dnpe05w0qvd0.jpg)

3. Install the module using npm `npm install @azure/cosmos`
4. Close the window and go back to create your **deleteTodo** function.

5. We configure only the index.js file.
```javascript
const cosmos = require('@azure/cosmos');
const endpoint = process.env.COSMOS_API_URL;
const key = process.env.COSMOS_API_KEY;
const { CosmosClient } = cosmos;

const client = new CosmosClient({ endpoint, key });
const container = client.database("dbtodos").container("items");

module.exports = async function (context, req) {
    const id = req.query.id;
    let res;
    try{
        res = await container.item(id).delete();
        context.res = {
            body: {'result': 'success'}
        };
    }catch(err){
        context.res = {
            status: 400,
            body: {'result': 'error'}
        };
    }
    
```
This is how the code works:
1. Create variables to import the Cosmos DB module and make reference to the application settings (COSMOS_API_URL y COSMOS_API_KEY).
2. Create a new instance of `CosmosClient` and set the key and endpoint.
3. Get the reference to the container and item.
4. Finally, execute the delete() method t finish the procedure.

## Summary
Using Azure Functions with Cosmos DB allow us to execute queries and operations with minimal code and effort. This is just an introduction to understand how the bindings work and how easy is to connect to your database using a serverless service.