loading...

Cosmos DB CRUD operations using Azure Functions

vidamrr profile image Marcos Rivas ・7 min read

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

  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

  6. 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
  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. Alt Text
  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

  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

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
    • Function App name: function-crud-cosmosdb
    • Publish: Code
    • Runtine stack: Node.js
    • Version: 12
    • Region: Central US
  3. For Hosting section: Alt Text
    • 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

  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

Get items

  1. Once the function is created, click on Functions and click on + Add. Alt Text
  2. Choose HTTP trigger so the function activates on every HTTP request. Alt Text
  3. Give a name to your function, mine is called getTodos. Authorization level is Anonymous. Alt Text
  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:
{
  "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.

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

Insert a new item

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

{
  "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
}
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:

{
  "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:

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

  2. Go to Debug console > CMD and then in the diles list to site > wwwroot
    Alt Text

  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.

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.

Discussion

pic
Editor guide
 

Great job man!