In this article, we will learn How to Build a serverless CRUD app with Azure function and Cosmos DB. We will walk you through the entire process of creating a restful CRUD API hosted on Azure cloud infrastructure. We will also leverage serverless technologies, specifically Azure functions and Cosmos in serverless capacity mode. Azure Functions is a serverless service that allows you to offer APIs to do certain activities. Today we will look at an example of retrieving data from an Azure SQL using the Azure function API. With a few small adjustments, you can utilize the API to do almost every CRUD activity on the database. We’ll use Postman to launch our API. Please read my previous article on How to create Azure Functions using Azure Portal.
Please find the source code on this link.
What do we cover in this article?
Here we are going to create a Product information CRUD using the below tools.
- Create the Azure Function CRUD locally and publish it to the Azure Portal
- Azure Cosmos DB to handle the CRUD Operation
- End-to-end testing with Postman
What is Azure Functions?
Azure function apps are a serverless compute service offered by Microsoft Azure that allows developers to group multiple Azure functions as a logical unit for easier management, deployment, scaling, and resource sharing. Function apps provide an efficient and scalable way to develop cloud-based serverless applications without worrying about the underlying infrastructure.
Azure Functions enables you to run short chunks of code (referred to as “functions”) without having to worry about application infrastructure. The cloud infrastructure delivers all the up-to-date servers you need to keep your application functioning at scale using Azure Functions.
Azure functions are event-driven, which means that you can run your function code when an event is triggered from either an existing on-premise service, any other Azure service, or a third-party service. The Azure function can be scaled, and you need to pay only for the resources you consume. Azure functions support many trigger points, such as HTTP triggers, queue triggers, etc.
Creating Azure Functions Using Visual Studio
- Launch the Visual Studio IDE and click on “Create new project”.
- In the “Create new project” window, select “Azure Function” from the template list.
- Click Next. In the “Azure Function” section, specify the function worker as “.NET 9.0 Isolated” (this is the latest version to date) and select Function as “HTTP Trigger” and Authorization level make as “Function“.
- In the below image, you can see the prompt to select what type of Azure function we want to create. Though we want to use the REST API for this demo that is the reason we choose “HTTP Trigger” and click to add.
Creating the Azure Cosmos DB and Get the ConnectionString
Let’s create an Azure Cosmos DB to connect our Azure function. We have created the NoSQL DB name “ProductDB” using the following steps. You can go through the below images and learn how we can set up the cosmos db.
- Here the cosmos db account name we set as “demo-cosmos-db-azure-function”
- We have set as database ID named as “ProductDB”
- The container id is “Items”
- Partition Key as
/category
. The product category may be Laptop, Mobile, Television etc..
Here below, we have created the containers, the below two images are one step to create the container.
- Database id: This is the database name assigned to a Cosmos DB database.
- Database throughput: This determines the capacity for handling read and write operations on the database (container). You can set it to automatically scale or to manual scaling.
- Database Max RU/s: RU/s stands for Request Units per Second. Request Units (RUs) are a way to quantify the amount of resources consumed by database operations, including reads, writes, and queries. Request units are the currency for database operations in Cosmos DB. An RU is the cost of the combined system resources needed to read 1 KB item.
- Container id: This is the name assigned to a collection in a Cosmos DB database.
- Indexing: You can set this property to Cosmos DB automatically indexes every property for all items in your container or you can choose to not create any indexes for the properties.
- Partition key: This is the partition key in a container, that is used to distribute and organize data across multiple logical partitions.
Get the Connection String of Azure Cosmos DB
As per highlight, you can get the connection string here.
Implementing the Azure Function CRUD with Cosmos DB
Adding the below packages to connect with Cosmos DB and run the Azure Function
Install-Package Microsoft.Azure.Cosmos
Install-Package Microsoft.Azure.Functions.Extensions
Install-Package Microsoft.Azure.Functions.Worker
Install-Package Microsoft.Azure.Functions.Worker.Extensions.Http
Install-Package Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore
Install-Package Microsoft.Azure.Functions.Worker.Sdk
Install-Package Newtonsoft.Json
Creating the Product Models
We have created the product
the model class that holds the Id
as the primary key and the category
as the portion key of the table.
public class Product
{
[JsonProperty("id")]
public string Id { get; set; } = Guid.NewGuid().ToString();
public string Name { get; set; }
public int? Quantity { get; set; }
public decimal? Price { get; set; }
[JsonProperty("category")]
public string Category { get; set; }
public string Description { get; set; }
public DateTime Created { get; set; } = DateTime.Now;
public bool Updated { get; set; }
}
Adding the connection string details on local.settings.json
Added the connection string like below, copied the end-point details from cosmos db, and add here. For security purposes, I added a * mark for the Account key.
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
"Logging__LogLevel__Default": "Information",
"CosmosDBConnectionString": "AccountEndpoint=https://demo-cosmos-db-azure-function.documents.azure.com:443/;AccountKey=**********************;"
}
}
Register the Dependency Injection to read the connection string in the Program.cs
using Microsoft.Azure.Cosmos;
using Microsoft.Azure.Functions.Worker.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
var builder = FunctionsApplication.CreateBuilder(args);
// Register the CosmosClient with the dependency injection container
builder.Services.AddSingleton<CosmosClient>(sp =>
{
var cosmosDbConnectionString = Environment.GetEnvironmentVariable("CosmosDbConnectionString");
return new CosmosClient(cosmosDbConnectionString);
});
// Register other services if needed
// Register logging services
builder.Services.AddLogging();
// Build the application
builder.Build().Run();
- Line #9-13: Here we register the dependency injection.
- Line #11:
Environment.GetEnvironmentVariable("CosmosDbConnectionString")
read the connection string that added to thelocal.settings.json
.
Creating the Azure Function class to implement the CRUD
Creating the Product End-Point
public class ProductEndPoint
{
private readonly ILogger<ProductEndPoint> _logger;
private readonly CosmosClient _cosmosClient;
private Container documentContainer;
public ProductEndPoint(ILogger<ProductEndPoint> logger, CosmosClient cosmosClient)
{
_logger = logger;
_cosmosClient = cosmosClient;
documentContainer = _cosmosClient.GetContainer("ProductDB", "Items");
}
[Function("CreateProduct")]
public async Task<IActionResult> CreateProductItem(
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "product")] HttpRequestData req)
{
if (req == null)
{
_logger.LogError("HttpRequest is null");
return new BadRequestResult();
}
_logger.LogInformation("Creating Product Item");
string requestData = await new StreamReader(req.Body).ReadToEndAsync();
var data = JsonConvert.DeserializeObject<Product>(requestData);
var item = new Product
{
Name = data.Name,
Category = data.Category,
Quantity = data.Quantity,
Price = data.Price,
Description = data.Description
};
await documentContainer.CreateItemAsync(item, new Microsoft.Azure.Cosmos.PartitionKey(item.Category));
return new OkObjectResult(item);
}
}
- We created the End-Point named as
ProductEndPoint
and register theCosmosClient
in constructor dependency along with documentContainer. - Line #10:
_cosmosClient.GetContainer("ProductDB", "Items")
It read the ProductDB with NoSQL DB. - Line #12-37: Function
CreateProduct
responsible for creating the product information. - The
[Function("CreateProduct")]
attribute declares this method as an Azure Function namedCreateProduct
.This is required for Azure Functions in isolated mode (Microsoft.Azure.Functions.Worker
) - The
[HttpTrigger]
attribute binds the function to an HTTP trigger.AuthorizationLevel.Anonymous
allows anyone to access the function without authentication. The HTTP method is"post"
, so the function will only respond to POST requests. TheRoute = "product"
sets the URL path for the function, e.g.,http://<base-url>/api/product
- Checks if the
HttpRequestData
(req
) object is null. If it is null, logs an error and returns a400 Bad Request
response.
Here on below, we have requested the create product.
Read Update Delete operation Azure Function
public class ProductEndPoint
{
private readonly ILogger<ProductEndPoint> _logger;
private readonly CosmosClient _cosmosClient;
private Container documentContainer;
public ProductEndPoint(ILogger<ProductEndPoint> logger, CosmosClient cosmosClient)
{
_logger = logger;
_cosmosClient = cosmosClient;
documentContainer = _cosmosClient.GetContainer("ProductDB", "Items");
}
[Function("GetAllProducts")]
public async Task<IActionResult> GetShoppingCartItems(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "product")] HttpRequestData req)
{
_logger.LogInformation("Getting All Product Items");
var items = documentContainer.GetItemQueryIterator<Product>();
return new OkObjectResult((await items.ReadNextAsync()).ToList());
}
[Function("GetProductItemById")]
public async Task<IActionResult> GetShoppingCartItemById(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "product/{id}/{category}")]
HttpRequest req,string id, string category)
{
_logger.LogInformation($"Getting Product Item with ID: {id}");
try
{
var item = await documentContainer.ReadItemAsync<Product>(id, new Microsoft.Azure.Cosmos.PartitionKey(category));
return new OkObjectResult(item.Resource);
}
catch (CosmosException e) when (e.StatusCode == System.Net.HttpStatusCode.NotFound)
{
return new NotFoundResult();
}
}
[Function("UpdateProduct")]
public async Task<IActionResult> PutProductItem(
[HttpTrigger(AuthorizationLevel.Anonymous, "put", Route = "product/{id}/{category}")] HttpRequestData req,
string id, string category)
{
_logger.LogInformation($"Updating Product Item with ID: {id}");
try
{
// Read request body
string requestData = await new StreamReader(req.Body).ReadToEndAsync();
var data = JsonConvert.DeserializeObject<Product>(requestData);
var partitionKey = new Microsoft.Azure.Cosmos.PartitionKey(category);
// Fetch the existing item from Cosmos DB
var itemResponse = await documentContainer.ReadItemAsync<Product>(id, partitionKey);
if (itemResponse.StatusCode == System.Net.HttpStatusCode.NotFound)
{
return new NotFoundResult();
}
// Update fields in the existing item
var existingItem = itemResponse.Resource;
existingItem.Updated = data.Updated;
existingItem.Name = data.Name ?? existingItem.Name;
existingItem.Price = data.Price ?? existingItem.Price;
existingItem.Description = data.Description ?? existingItem.Description;
// Upsert the updated product
var upsertResponse = await documentContainer.UpsertItemAsync(existingItem, partitionKey);
// Explicitly fetch the updated item to ensure latest data
var updatedItemResponse = await documentContainer.ReadItemAsync<Product>(id, new Microsoft.Azure.Cosmos.PartitionKey(category));
// Return the updated item
return new OkObjectResult(updatedItemResponse.Resource);
}
catch (Microsoft.Azure.Cosmos.CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
{
_logger.LogWarning($"Product with ID: {id} not found.");
return new NotFoundResult();
}
catch (Exception ex)
{
_logger.LogError($"Error updating product item: {ex.Message}");
return new StatusCodeResult(StatusCodes.Status500InternalServerError);
}
}
[Function("DeleteProduct")]
public async Task<IActionResult> DeleteProductItem(
[HttpTrigger(AuthorizationLevel.Anonymous, "delete", Route = "product/{id}/{category}")] HttpRequestData req,
string id, string category)
{
_logger.LogInformation($"Deleting Product Item with ID: {id}");
await documentContainer.DeleteItemAsync<Product>(id, new Microsoft.Azure.Cosmos.PartitionKey(category));
return new OkResult();
}
}
- Here we have created the Read, Update, and Delete operation.
product/{id}/{category}
is used to get the id as the primary key and the category as the partition key.
Test with Postman
Run the Azure function and you can see the below functions we can test using Postman.
Publishing the Azure function using Visual Studio
On Visual Studio, Right-click on the project and click publish, and select publish method as Azure function.
That’s it for this article.
Source Code
Please find the source code on this link.
Conclusion
In this article, we discussed How to Build a serverless CRUD app with Azure function and Cosmos DB. We walked through the entire process of creating a restful CRUD API hosted on Azure cloud infrastructure. We will also covered serverless technologies, specifically Azure functions and Cosmos in serverless capacity mode.
Leave behind your valuable queries and suggestions in the comment section below. Also, if you think this article helps you, do not forget to share this with your developer community. Happy Coding 🙂
Latest Articles
- How to Build a serverless CRUD app with Azure function and Cosmos DB
- How to resolve Function App Cannot Create within Azure Portal
- How to convert Text To Speech With Azure Cognitive Services using Angular and .Net Core
- Building CI/CD Pipeline(YAML) using Azure DevOps – GitHub to Azure Web App (PaaS)
- Upload Download and Delete files in Azure Blob Storage using ASP.NET Core and Angular
- How to upload files to Azure Blob Storage using Asp.Net Core Web API
- Introduction to Azure Cosmos DB
- How to create Cosmos DB in Azure
- Logging into Azure App Service with ASP.Net Core
- Intro to Azure Logic Apps- Complete Guide
- How to use Azure Key Vault in ASP.Net Core
- How to create Azure Functions using Azure Portal
- How to Build ASP.Net Core API using Azure Cosmos DB Local Emulator
- How to Send Emails from ASP.NET Core using Azure
- Create a Blazor Web-Assembly App using Azure Function and deploy it as an Azure Static App
SUPPORT ME