Basic API application with Azure Cosmos
This article provides a tutorial to create a basic API application with CosmosDb. The domain of this API application is deployment management. After this article, you will be able to create a CRUD RESTful API in .NET 6 and how to read, insert, delete, and update from Azure Cosmos Db using .NET 6 C#.
_2.png)
1. Install Azure Cosmos Db Emulator
- You can try this link to download: Install and develop locally with Azure Cosmos DB Emulator
- Step-by-step: follow this topic
2. Step by step
This topic will use .NET 6.0 & Visual Studio 2022
Step 1: New project
- Create ASP .NET Core Web API
- Input project name
- Select framework
- Result
- Add infrastructure folders

- Enums: Store enumerations, default values, and constant values here.
- Models: Contain DTOs, object definitions: API response, app settings, etc.
- Options: Cosmos client options will be saved here.
- QueryBuilders: Define queries for CosmosDb here.
- Services: we could gather services here: cosmosDb, deployment services, etc.
Step 2: Add cosmosdb services
- CosmosDb services will have the following:
- ILogger: used to log information, error, and warning.
- IConfiguration: we stored the database connection in the configuration file, and we need IConfiguration to read data from the configuration file.
- Define services here:
- The first function we need is: GetConnection()

- Private function, cannot be called outside CosmosDb Services
- Get CosmosConnection by using the connection string, environment, and client options from app settings.
- Steps:
- Create CosmosClient by using a connection string and cosmos client options
- Get the database by using CosmosClient.
- Then, we will need a get container function: GetContainer(string containerName)

- The same with GetConnection(), this one is a private function as well.
- We need to create container first, then container will be used to execute query, update, insert, or delete the item.
- At database explorer, click on … to create new container

- Input container name and partition key

- Input provision throughput

- Execute:
- Get container then execute.
- Log the request charge.
- The asynchronous function name should have “async” suffix.
- Get list items

query items
- Using query definition. For example: select * from c order by c._ts DESC
- Need to know the container name. In the Azure Cosmos Database, data is stored as documents in containers.
- Update item

update item
- Container name is mandatory
- The partition key acts as a logical partition, every single object has to have a property, which is considered the partition key. In this topic, we consider Type as the partition key. That means Type will exist on every single object.
- Selecting a partition key for container is a very important design choice in Azure Cosmos DB. Be careful when selecting which property is the partition key. For example, if id serves as the partition key, and we have over 1000 documents in the container, and there are over 1000 unique id, that means we will have over 1000 partition keys, and the container will be split into over 1000 logical partitions. That’s why the partition key should have a high cardinality. To put it simply, the partition key should have a wide range of possible values.
- Partition key has two components: partition key path and partition key value. In this topic, partition key path = /Type, partition key value = value of Type property.
- Type value cannot be updated.
- For example:
{
"id": "b9572c40-56bc-42b4-8bee-8f8dbd650435",
"Type": "DeploymentRequest"
}
- partition key path = /Type
- partition key value = DeploymentRequest
- DeploymentRequest cannot be changed
- The JSON specification calls out that the partition key should only contain string values or you will get into trouble with interoperability problems.
- Insert item

insert item
- Container name is required
- Item is any object we want, with Type property because Type is the partition key
- If an item does not have an id, then after the item is inserted successfully, an id will be generated automatically with other system properties: _rid, _self, _etag, _attachments, _ts
- Example:
{
"id": "b9572c40-56bc-42b4-8bee-8f8dbd650435",
"Type": "DeploymentRequest",
"Version": "v4.0.0",
"VersionStatus": "Missing",
"Environment": "DEV",
"Status": "ResourcePending",
"CreatedUser": "[email protected]",
"DeploymentDate": "2022-03-22T07:28:06.947Z",
"CreatedDate": "2022-03-22T07:28:13.2721979Z",
"Latest": true,
"HasApprove": false,
"_rid": "ug1nALpRWxcBAAAAAAAAAA==",
"_self": "dbs/ug1nAA==/colls/ug1nALpRWxc=/docs/ug1nALpRWxcBAAA==/",
"_etag": "\"00000000-0000-0000-d6e9-2895e3e001d8\"",
"_attachments": "attachments/",
"_ts": 1664774990
}
- Delete an item by id

delete item
- Container name and partition key is mandatory
- id is required, cosmos will delete the document based on document id
- Example:
- id = "b9572c40-56bc-42b4-8bee-8f8dbd650435"
- partitionKey = "b9572c40-56bc-42b4-8bee-8f8dbd650435"
- containerName = "deployment"
Step 3: Add deployment services
- CosmosServices class is a dependency of deployment services class.

- Cosmos services are used to save/get deployment request into/from the database
- Get deployment requests

- Define query as QueryDefinition
- Ex: Get all latest documents with type = DeploymentRequest, order by _ts descending
SELECT VALUE c
FROM c
Where c.Type = @Type and c.Latest = true
ORDER BY c._ts DESC
@Type = “DeploymentRequest”
- Provide container name = deployment
- Partition Key is optional
- Insert deployment

- input param is the deployment object.
- when inserting, that’s means we are creating a new document, no need to have an id property, if it is missing, it will be generated automatically.
- We should provide container name, and partition key when using cosmos services to insert deployment into database. Because we are considering the input used to insert is an object, we should not get partition key value from the input.
- Ex:
{
"Type": "DeploymentRequest",
"Version": "v4.0.0",
"VersionStatus": "Missing",
"Environment": "DEV",
"Status": "ResourcePending",
"CreatedUser": "[email protected]",
"DeploymentDate": "2022-03-22T07:28:06.947Z",
"CreatedDate": "2022-03-22T07:28:13.2721979Z",
"Latest": true,
"HasApprove": false
}
- Update deployment

- Input param is the deployment object
- When updating, id, and type are required. If id is missing, container cannot update document, and we will get an error message.
Step 4: Add deployments controller
- DeploymentServices class is a dependency of the deployment controller class.

- The result will be converted to OkObjectResult.
- Define APIs here:
- Get all deployments

- HTTP GET method.
- No need param, we can pass params for pagination purposes later.

- Get deployment by Entity Id

- HTTP GET method.
- EntityId is required. In this topic, we manage documents by state base, that means document will have an id and entityId.
- After the document is updated, id will be generated new, entity id will not be changed, every document will have Latest property.
- The latest document will have Latest = true, and Latest = false for the rest. In this way, we can track the history change of the document.
- The query used to get document by entity id is:
SELECT VALUE c FROM c
Where c.EntityId = @EntityId and c.Latest = true
- Value of @EntityId is entityId param.

- Update deployment

- HTTP PUT method should be used when updating something.

- Insert deployment

- HTTP POST method should be used when inserting something.
- System properties are not required
