Explore cosmos with Serverless
Chris Noring
Posted on June 29, 2019
Follow me on Twitter, happy to take your suggestions on topics or improvements /Chris
You thought this was about space didn't you? Yea my bad :). But now that I have your attention let's talk about Serverless and how we can use that with Azure Cosmos DB. It's so easy it's out of this world ;)
What we are going to do is show:
- Serverless and how to create a function in the Cloud
- Create a Azure Cosmos DB database
- Show how can read and write data to your Azure Cosmos DB
Serverless - look Ma no servers
Serverless is the new black, the thing that everybody talks about and for good reason. Gone are the days when you had that server room with no oxygen and full of cables and lord knows what else.
Now you can just focus on the code and rest assured that your code lives in someone else server room ;), that is the Cloud. The point is, it's not a thing you need to care about anymore.
Serverless is, of course, a bit more than just no servers, it's fully managed, there is not even a web server to configure. I've covered the whys and what offerings in this article, so if you are interested, go and have a read.
We will focus on a specific offering namely Azure Functions.
The reason it's chosen for this specific article is:
- Easy to get started with , comes with great extensions for all major IDEs including VS Code that lets you scaffold functions, debug and more.
- Creates a connection to your Azure Cosmos DB database so you can just focus on changing the data and not having to bother with instantiating connections
Resources
- Free account Azure account You will need to sign up on Azure to use Azure CLI and deploying Azure Functions
- How to install azure cli For some of the activities, we do we will use Azure CLI in the terminal. This is a great way to manage your resources.
- Create an Azure function in VS Code, This will show you how to build your first Azure Function in VS Code
- Working with Azure Cosmos DB + Azure function
- Working with Azure Cosmos DB and Node.js This article takes you through building a Node.js app, create an Azure Cosmos DB app and connect the two. Create an Azure Cosmos DB using Azure CLI This shows how you can set up your Cosmos DB database using the Azure CLI Create an Azure Cosmos DB using Azure CLI + additional resources This shows all the different resources you might need to create before and during setting up Azure Cosmos DB, a bit longer version than the above link
- Series I wrote on getting started with Serverless
- Serverless CRUD with Azure Cosmos DB client This is a repository by my colleague Simona Cotin that shows how you can do a full CRUD using the MongoDB client
- Reference page for Serverless + Azure Cosmos DB at GitHub This is the most complete page there is on the combination Serverless + JavaScript + Azure Cosmos DB
- Slack WebHooks
- Better looking Slack messages with attachments
Steps
Let's look at this from a mile-high view and see what we need to do, to accomplish what we want. We need to:
- Scaffold a Azure Cosmos DB database in Azure
- Create a Serverless app and create an Azure Function for each call. We show how you can read data, create it and update it
- Configure our functions, to connect to our database, so working with the database is made real easy
Create a Azure Cosmos DB database
Ok. There are two ways to do this. We can either:
- Create it through the Azure CLI
- Use the portal and create the database
Let's show how to do it in the Azure-CLI
at first and then switch to the terminal gradually. Let's face it, it's great to feel like a hacker but sometimes you want to see what you are doing. You also want to make sure it's all there and some things are more practical to do in a UI, like data entry.
Using Azure CLI
To create the database we need to take the following steps:
- Resource group, Create a resource group for our database or use an existing group
- Azure Cosmos DB, Create a Azure Cosmos DB account
- Database, Create the database
- Add collections and data
Create the resource group
az group create \
--name mycosmosgroup \
--location "West Europe"
The above says "provisioningState": "Succeeded"
, which means we succeeded in creating our resource group.
Create the Azure Cosmos DB Account
Next step is about creating our own account. The account name should be lowercase.
az cosmosdb create \
--resource-group mycosmosgroup \
--name cosmosaccount-chris-sql \
--kind GlobalDocumentDB \
--locations "North Europe"=0 "West Europe"=1 \
--default-consistency-level "Session" \
--enable-multiple-write-locations true
This might take a little while. All databases take a little time to scaffold.
If you are impatient you can go visit it in the portal and it should look something like this:
Last step is to create the database.
Creating the database
Ok, we have a database account but we don't have a database, yet. Now, to create a database we need to create something called a container. Let's head to the portal and find our database account. Now click the tab Data Explorer
. It should now look something like:
By hitting the indicated button we get a dialog opened that's asking us to create a new container, like so:
Fill in values for the highlighted parts. You will notice that the Partition key
will prepend the value with a /
so id
, becomes /id
.
I know what you are thinking, what the heck is a partition key? Let's see what the info icon tells us:
Partitions are a chapter all to itself. If you are really interested in knowing more have a look at this article
For now, we are happy knowing it's a column we point out in our table.
Ok, so we created our container and we should have something looking like this:
Now what? Now we fill it with data and we do that by clicking the New Item
button. It should present you with an edit view like so:
So change the item to say:
{
"id": "1",
"name": "tomato"
}
Now hit Save
and repeat the procedure and create another item with the value { "id": "2", "name": "lettuce" }
.
We want to show this data in a Serverless function right? Right?
Yea I thought so :)
So Serverless is next.
Serverless - Reading and Writing to/from our database
Ok then, Serverless part it is. We need to support interacting with our Azure Cosmos DB database
We will build the following:
- An Azure Function App, this will hold all of our functions
- Three different Azure Functions, each supporting an HTTP verb
- in/out bindings to Azure Cosmos DB, this is configuration we write that creates a database connection for us
Create an Azure Function App
To be able to use Azure Functions they need to exist inside of an Azure Function app. We will create it using VS Code so we need to make sure we have the correct extension installed.
So look for Azure Functions
in the extension area. It should look like this:
Next step is to scaffold our app.
We do that by opening up the command palette. That is done by either selecting View > Command Palette
in the menu or you can hit CMD + SHIFT + P
, if you are on a Mac.
Select the current directory, JavaScript as language and HTTP trigger. The last will ensure that your project contains at least one function. Name the function ProductsGet
and give it Anonymous
as authorization level.
Create remaining functions
For this step, we need to bring up our command palette again. CMD + SHIFT + P
. Select Azure Functions: Create Function
and select HttpTrigger
. Name the function ProductsCreate
.
Now repeat this process and create another function called ProductsUpdate
We should now configure the allowed HTTP methods for each function. Go into each function directory and open up function.json
. There you will find bindings
and specific config looking like this:
"methods": [
"get",
"post"
]
Ensure you configure it in the following way:
-
ProductsGet
, should only supportget
-
ProductsCreate
, should only supportpost
-
ProductsUpdate
, should only supportput
Configure functions with Azure Cosmos DB
Next up we need to revisit the function.json
for each function. This time we need to add a binding that lets us talk to our Azure Cosmos DB database.
Essentially we need to create something called a binding that not only holds a connection to our Azure Cosmos DB database but also easily lets us do a CRUD on a specific collection.
The configuration looks pretty much like this:
{
"name": "productDocument",
"type": "cosmosDB",
"databaseName": "ToDoList",
"collectionName": "Items",
"createIfNotExists": true,
"connectionStringSetting": "CosmosDB",
"direction": "in"
}
Depending on what we are trying to do we either need a binding of "direction": "in"
or for it to be binding of type out
, in which case it would look like this "direction": "out"
.
If we only want to read data we need this to say "direction": "in"
. Creating we need it to say "direction": "out"
. Updating is a different matter, in fact, we need two sets of bindings, one that retrieves our record and one to create/replace our record, more on that later.
So what are we saying here? We are saying that we have a databaseName
called TodoList
, change that to whatever you called your database. We also have a collectionName
called Items
which you could see in the Azure portal was the case. We also define it as direction
having the value in
. This means that this will set up the connection for us and whatever object we are given will contain the data we need.
There is one more thing here connectionStringSetting
pointing to the value CosmosDB
, now this is an AppSetting we set up in our Azure Functions app project. We haven't deployed it to Azure yet so we need to store this somewhere, for now, that somewhere is in the file local.settings.json
. It should look like this currently:
"IsEncrypted": false,
"Values": {
"FUNCTIONS_WORKER_RUNTIME": "node",
"CosmosDB": "[connection string to our database]"
}
}
You need to go to the portal and our created database, click the tab menu Keys
and copy the value in the field Primary Connection String
and then set it to the value of above key CosmosDB
.
Additional configuration
We are not quite done there I'm afraid. There are two more things we need to do:
- Dependencies, set up dependencies to be installed
- Storage account, set up a storage account
The first bit we solve by opening up the file host.json
and ensure it has the following content:
{
"version": "2.0",
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[1.*, 2.0.0)"
}
}
The key and value for extensionBundle
instruct Azure that it should be installing all needed libraries so we can talk to queues, databases and pretty much all cool things we can integrate our Azure Function App with as input and output bindings.
The second and last thing we need to do is to create a storage account. We can easily do so in the portal by clicking Add Resource
, type Storage Account
and follow the instructions. Then we need to obtain the connection string and we need to add an entry to the file local.settings.json
, like so:
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "[storage account connection string]",
"FUNCTIONS_WORKER_RUNTIME": "node",
"CosmosDB": "[database connection string]"
}
}
Write some code
At this point, we want to write some code in our ProductsGet
functions. So we need to edit ProductsGet/index.js
to say the following:
module.exports = function (context, req) {
for(var i =0; i< context.bindings.productDocument.length; i++) {
let product = context.bindings.productDocument[i];
context.log('id', product.id);
context.log('name', product.name);
}
context.res = {
// status: 200, /* Defaults to 200 */
body: "Ran ProductsGet"
};
};
Looking at code above we see that context.binding.productDoucment
is containing a list of our Items
. We iterate that list in the code and print out id
and name
. Because of configuration like this in function.json
:
{
"name": "productDocument",
"type": "cosmosDB",
"databaseName": "ToDoList",
"collectionName": "Items",
"createIfNotExists": true,
"connectionStringSetting": "CosmosDB",
"direction": "in"
}
we establish a connection to our Azure Cosmos DB database and a specific collection Items
and ensure to create a handle productDocument
. Imagine having to write the connect to database code yourself, not fun right?
Deploy and test it out
Ok, next step is to deploy it. To deploy it we need to do two things:
- Deploy the Azure Function app, this is as simple as clicking a button in VS Code
- Deploy the local app settings to Azure, this is needed to ensure our AppSettings in Azure correctly points out our Azure Cosmos DB database but also our storage account.
Let's start with the deploy part. Ensure you have the following extension installed in VS Code:
This will enable you to easily deploy anything to Azure. We started this article talking about another extension namely Azure Functions
. Ensure you have them both installed and life will be a lot simpler.
Now as for the deployment part. Start by clicking the highlighted Azure icon below
Then we come here:
Indicated at the top right is an icon looking like a flash. Clicking that will deploy the app to Azure.
Afterward, it will become part of the list below the flash icon. It's also where we would go to redeploy changes.
The last step is to ensure we upload all the local app settings to Azure. To do that we need to talk our deployed Azure Function App and right-click it and choose to upload them. It should look like so:
At this point, the app should actually work. So lets head to the Azure Portal and try it out:
Just click on the correct function ProductsGet
then click Run
. This will produce a terminal window below that will print the following:
As you can see from the above image it is reading from the Azure Cosmos DB database.
Awesome right, we have a working connection. Now to talk the remaining operations.
Adding remaining operations
So far we have been reading so how to support these additional operations? Well, we need to configure each function.json
and give it the correct type of configuration and we, of course, need to go into each index.js
and add the necessary code to either Create or Update.
Supporting creation
So far we have been supporting how to read data from our database. We accomplished that by creating a binding that had a direction
value of in
and we also needed to point out our database and what collection to read from.
Creating something is about creating a binding with direction
having the value out
. This gives us a handle that we can add data to.
Let's start with the configuration part. In the file ProductsCreate/function.json
we add the following entry to our bindings array:
{
"name": "productDocument",
"type": "cosmosDB",
"databaseName": "ToDoList",
"collectionName": "Items",
"createIfNotExists": true,
"connectionStringSetting": "CosmosDB",
"direction": "out"
}
Ok that sets us up nicely so we can focus on the second step, adding code to ProductsCreate/index.js
:
module.exports = function (context, req) {
const { id, name } = req.body;
// creating doc
context.bindings.productDocument = JSON.stringify({
id: id,
name: name
});
// saving doc
context.done();
};
Note how the configuration value for they key name
in function.json
matches context.bindings.productDocument
. What we do then is to assign an object to productDocument
:
context.bindings.productDocument = JSON.stringify({
id: id,
name: name
});
Above we are creating the record we want to be inserted. To actually do the save we end it all with the command context.done()
We don't believe this until we tried it right? The first thing we need to do is to redeploy our project to Azure. Because we deployed it once already, it's super simple to redeploy. Click the Azure extension on the left panel then right-click your Azure Functions app project and select Deploy to Function App
like below:
This will redeploy it all. Wait for it to finish.
Done? good let's continue
For this one, let's head to the Azure Portal and test it (you can use cURL or any client that supports sending REST calls).
In the portal we want to select our function and click the Test
tab on the right:
Now enter a request like so:
Next step is to click the Run
button. We should be getting a result like the below
To really verify that this worked, head to the resource page for your database and see if you have a third record:
There it is ! :)
That's it for creation, let's talk about updating next.
Supporting update
Updating a record is a different matter. We need to support two bindings. Why is that you ask?
Well, we need a binding of type in
so we can retrieve the record we need.
Then we need a second out
binding to actually replace the record.
Hmm ok, show me
Ok then, bindings first, they should look like this:
{
"name": "productDocument",
"type": "cosmosDB",
"databaseName": "ToDoList",
"collectionName": "Items",
"createIfNotExists": true,
"connectionStringSetting": "CosmosDB",
"direction": "out"
},
{
"name": "productDocumentsIn",
"type": "cosmosDB",
"databaseName": "ToDoList",
"collectionName": "Items",
"createIfNotExists": true,
"connectionStringSetting": "CosmosDB",
"direction": "in"
}
now to the code:
module.exports = function (context, req) {
const {
id,
name
} = req.body;
// or you could limit the in to only be one doc
const foundDoc = context.bindings.productDocumentsIn.find(p => p.id == id);
// do the update
context.bindings.productDocument = foundDoc;
context.bindings.productDocument.name = name;
context.done();
context.res = {
// status: 200, /* Defaults to 200 */
body: "Record updated"
};
};
What we can see is how we first retrieve the record from our input binding productDocumentsIn
and assign it to the variable foundDoc
. Next thing we do is assign foundDoc
to our out
handle found on context.bindings.productDocument
. We do this to ensure we don't lose any values on this record should or values on the body only represent a partial update. Next thing we do is take the name
that came with our body
and apply that, like so context.bindings.productDocument.name = name
and of course we finish that off with a context.done()
.
That's it, that is how we support updating.
Summary
We started off by creating a Azure Cosmos DB database. Then we put some data in there. Next, we created some serverless functions. Thereafter we learned about bindings and how those could save us time when it came to creating a connection towards our database. Thanks to the bindings we ended up writing quite little code to support reading and writing.
How about you try this too? :)
Posted on June 29, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.