Reading Azure Cosmos DB Data In Azure Functions

This is the second part in a series of articles.

In addition to triggering a function when Cosmos DB data changes (as we saw in part one) you can also read data in from Cosmos DB when a function executes. The simplest way to do this is to use an input binding.

The [CosmosDB] binding attribute can be used both as an input binding and an output binding. When used as an input binding it allows one or more documents to be retrieved from the database.

When using the attribute there are a number of ways to configure it including:

  • The Cosmos DB database name
  • The collection name
  • The document Id to retrieve
  • The partition key
  • And the connection string app setting

Reading a Single Cosmos DB Document in an Azure Function

The following code shows an Azure Function that is triggered from an HTTP GET request:

[FunctionName("GetDriver")]
public static async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req,
    [CosmosDB(databaseName: "pizza",collectionName: "driver", Id = "{Query.id}", PartitionKey = "{Query.storeId}", ConnectionStringSetting = "pizzaConnection")] Driver driver,
    ILogger log)
{
    log.LogInformation("C# HTTP trigger function processed a request.");

    if (driver is null)
    {
        return new NotFoundResult();
    }

    return new OkObjectResult(driver);
}

Notice first the [CosmosDB] binding attribute. The combination of the Id and PartitionKey will determine the Driver object (if any) that will be retrieved. Note the format of these two: {Query.id} and {Query.storeId}.This will look for query string parameters in the incoming HTTP GET called id and storeId, for example: http://localhost:7071/api/GetDriver?id=1&storeId=101

If no document is found, driver will be null.

If the document was found it will be returned as JSON to the caller.

Reading Multiple Cosmos DB Documents in an Azure Function Using SqlQuery

The following function will get the latest 100 drivers (as sorted by the built in timestamp _ts property):

[FunctionName("GetDrivers")]
public static async Task<IActionResult> GetDrivers(
    [HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req,
    [CosmosDB(databaseName: "pizza", collectionName: "driver", SqlQuery = "SELECT top 100 * FROM driver order by driver._ts desc", ConnectionStringSetting = "pizzaConnection")] IEnumerable<Driver> drivers,
    ILogger log)
{
    log.LogInformation("C# HTTP trigger function processed a request.");

    if (drivers is null)
    {
        return new NotFoundResult();
    }

    foreach (var driver in drivers)
    {
        log.LogInformation(driver.Name);
    }

    return new OkObjectResult(drivers);
}

There’s a couple of things to notice in the preceding code. The first is that instead of a single Driver, the binding now returns IEnumerable<Driver> drivers. Also notice that the binding no longer has Id and PartitionKey.

Reading Multiple Cosmos DB Documents Based on Query String Parameter

A more advanced technique is to bind to an instance of DocumentClient. This allows more fine grained/low level/more specific access of data, such as using a LINQ query to perform the search:

[FunctionName("GetDriversForStore")]
public static async Task<IActionResult> GetDriversForStore(
    [HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req,
    [CosmosDB( ConnectionStringSetting = "pizzaConnection")] DocumentClient client,
    ILogger log)
{
    log.LogInformation("C# HTTP trigger function processed a request.");
    
    // Validation and error checking omitted for demo purposes
               
    string storeId = req.Query["storeId"]; // read storeId to get driver for from querystring

    Uri driverCollectionUri = UriFactory.CreateDocumentCollectionUri(databaseId: "pizza", collectionId: "driver");

    var options = new FeedOptions { EnableCrossPartitionQuery = true }; // Enable cross partition query

    IDocumentQuery<Driver> query = client.CreateDocumentQuery<Driver>(driverCollectionUri, options)
                                         .Where(driver => driver.StoreId == storeId)
                                         .AsDocumentQuery();

    var driversForStore = new List<Driver>();

    while (query.HasMoreResults)
    {
        foreach (Driver driver in await query.ExecuteNextAsync())
        {
            driversForStore.Add(driver);
        }
    }                       

    return new OkObjectResult(driversForStore);
}

The preceding code returns all drivers that belong to a specific store passed in as a querystring storeId parameter.

In the next part of this series we’ll see how to write data out to Cosmos DB when a function executes.

If you want to fill in the gaps in your C# knowledge be sure to check out my C# Tips and Traps training course from Pluralsight – get started with a free trial.

SHARE:

Comments (3) -

  • Les Carbonaro

    10/24/2019 9:25:14 PM | Reply

    Is there a JavaScript version of this article?

  • Hayden Hancock

    3/31/2020 5:16:53 AM | Reply

    Do you have this available on GitHub? I am trying to figure out where you define the ConnectionStringSetting. You have it set to "pizzaConnection" but where is this actually defined?

    • Dariusz Dąbrowski

      7/4/2020 1:15:37 PM | Reply

      For local development with cosmos db emulator You should provide that settings in local.settings.json as folow:
      {
        ...
        "Values": {
          ...
          "pizzaConnection": "AccountEndpoint=https://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
        },
        ...
      }

Pingbacks and trackbacks (2)+

Add comment

Loading