To retrieve data from a Cosmos DB, its REST API can be used. The query is transmitted in the body of a POST request. Based on the current date, an authorization string must be calculated and supplied. The calculation is described in the Comos DB API documentation and an example implementation with .NET is provided via GitHub. A request for a collection called flights in a Cosmos DB called airport, which returns 2 simple data records, could look like this:

curl -i -X POST -H "x-ms-date: Mon, 27 Dec 2021 22:57:49 GMT" -H "x-ms-documentdb-query-enablecrosspartition: true" -H "x-ms-documentdb-isquery: True" -H "Content-Type: application/query+json" -H "x-ms-version: 2018-12-31" -H "Authorization: type%3Dmaster%26ver%3D1.0%26sig%3DPvzWELsQlEbu%2FMAuVhTUlpmpQ5%2BVIinZPqX1eJYcMB0%3D" -d '{ "query":"SELECT * FROM c" }' https://cosmosbyapim.documents.azure.com/dbs/airport/colls/flights/docs
HTTP/2 200
cache-control: no-store, no-cache
pragma: no-cache
content-type: application/json
server: Microsoft-HTTPAPI/2.0
strict-transport-security: max-age=31536000
x-ms-activity-id: 2eace994-841e-4ee0-ac4c-f626ac45798e
x-ms-last-state-change-utc: Sun, 26 Dec 2021 18:00:57.354 GMT
x-ms-resource-quota: documentSize=51200;documentsSize=52428800;documentsCount=-1;collectionSize=52428800;
x-ms-resource-usage: documentSize=0;documentsSize=0;documentsCount=2;collectionSize=0;
x-ms-schemaversion: 1.13
lsn: 3
x-ms-item-count: 2
x-ms-request-charge: 2.29
x-ms-alt-content-path: dbs/airport/colls/flights
x-ms-content-path: p6xBANHyrpY=
x-ms-documentdb-partitionkeyrangeid: 0
x-ms-xp-role: 1
x-ms-cosmos-query-execution-info: {"reverseRidEnabled":false,"reverseIndexScan":false}
x-ms-global-committed-lsn: 2
x-ms-number-of-read-regions: 0
x-ms-transport-request-id: 1
x-ms-cosmos-llsn: 3
x-ms-session-token: 0:-1#3
x-ms-request-duration-ms: 0.936
x-ms-serviceversion: version=2.14.0.0
x-ms-cosmos-is-partition-key-delete-pending: false
x-ms-gatewayversion: version=2.14.0
date: Mon, 27 Dec 2021 22:59:16 GMT

{
	"_rid": "p6xBANHyrpY=",
	"Documents": [
		{
			"id": "dd26ec39-d1ac-4cdf-b18d-70fbce0f276e",
			"direction": "arrival",
			"date": "2021-12-26T18:30:00Z",
			"gate": "gate 1",
			"flightnumber": "AB-123",
			"pilot": "Marcus Smith",
			"baggageclaim": "7",
			"_rid": "p6xBANHyrpYBAAAAAAAAAA==",
			"_self": "dbs/p6xBAA==/colls/p6xBANHyrpY=/docs/p6xBANHyrpYBAAAAAAAAAA==/",
			"_etag": "\"030047cd-0000-0d00-0000-61c8b31c0000\"",
			"_attachments": "attachments/",
			"_ts": 1640543004
		},
		{
			"id": "a6d5b3ea-169c-4174-bcea-19cda43ce882",
			"direction": "departure",
			"date": "2021-12-26T20:30:00Z",
			"gate": "gate 1",
			"flightnumber": "AB-321",
			"pilot": "Marcus Smith",
			"baggageclaim": "",
			"_rid": "p6xBANHyrpYCAAAAAAAAAA==",
			"_self": "dbs/p6xBAA==/colls/p6xBANHyrpY=/docs/p6xBANHyrpYCAAAAAAAAAA==/",
			"_etag": "\"030059cd-0000-0d00-0000-61c8b3f20000\"",
			"_attachments": "attachments/",
			"_ts": 1640543218
		}
	],
	"_count": 2
}

The dynamic calculation can also be done directly in a policy in Azure API Management. The APIM can provide a much simpler GET REST call and encapsulates the complexity of token generation. In addition, the sending of any queries to the Cosmos DB in the POST body can be prevented.

<policies>
    <inbound>
        <base />
        <set-variable name="cosmosQuery" value="@{
            JObject query = new JObject();
            query.Add("query", "SELECT * FROM c");
            return query.ToString(Newtonsoft.Json.Formatting.None);
        }" />
        <set-variable name="cosmosKey" value="{{comosDBReadKey}}" />
        <cache-lookup-value key="cachedUtcDate" variable-name="utcDateFromCache" />
        <cache-lookup-value key="cachedToken" variable-name="tokenFromCache" />
        <choose>
            <when condition="@(!context.Variables.ContainsKey("tokenFromCache") || !context.Variables.ContainsKey("utcDateFromCache"))">
                <set-variable name="utcDateFromCache" value="@{return DateTime.UtcNow.ToString("r");}" />
                <cache-store-value value="@((string)context.Variables["utcDateFromCache"])" key="cachedUtcDate" duration="300" />
                <set-variable name="authToken" value="@{
                    string date = (string)context.Variables["utcDateFromCache"];
                    byte[] key = Convert.FromBase64String((string)context.Variables["cosmosKey"]);
                    var hmacSha256 = new System.Security.Cryptography.HMACSHA256 { Key = key };
                    string payLoad = "post\ndocs\ndbs/airport/colls/flights\n"+date.ToLowerInvariant()+"\n\n";
                    byte[] hashPayLoad = hmacSha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(payLoad));
                    string signature = Convert.ToBase64String(hashPayLoad);
                    string token = System.Uri.EscapeDataString("type=master&ver=1.0&sig="+signature);
                    return token;
                }" />
                <cache-store-value value="@((string)context.Variables["authToken"])" key="cachedToken" duration="300" />
                <set-variable name="tokenFromCache" value="@((string)context.Variables["authToken"])" />
            </when>
            <otherwise />
        </choose>
        <set-variable name="requestPath" value="@((string)(context.Request.OriginalUrl.QueryString))" />
        <send-request mode="new" response-variable-name="response" timeout="10" ignore-error="false">
            <set-url>https://cosmosbyapim.documents.azure.com/dbs/airport/colls/flights/docs</set-url>
            <set-method>POST</set-method>
            <set-header name="Content-Type" exists-action="override">
                <value>application/query+json</value>
            </set-header>
            <set-header name="x-ms-documentdb-isquery" exists-action="override">
                <value>True</value>
            </set-header>
            <set-header name="Accept" exists-action="append">
                <value>application/json</value>
            </set-header>
            <set-header name="x-ms-version" exists-action="append">
                <value>2018-12-31</value>
            </set-header>
            <set-header name="x-ms-date" exists-action="append">
                <value>@((string)context.Variables["utcDateFromCache"])</value>
            </set-header>
            <set-header name="Authorization" exists-action="append">
                <value>@((string)context.Variables["tokenFromCache"])</value>
            </set-header>
            <set-header name="x-ms-max-item-count" exists-action="append">
                <value>10000</value>
            </set-header>
            <set-header name="x-ms-documentdb-query-enablecrosspartition" exists-action="override">
                <value>true</value>
            </set-header>
            <set-body>@((string)context.Variables["cosmosQuery"])</set-body>
        </send-request>
        <return-response>
            <set-body>@(((IResponse)context.Variables["response"]).Body.As<JObject>()["Documents"].ToString())</set-body>
        </return-response>
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
        <!-- <set-header name="unwanted header" exists-action="delete" /> -->
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

The actual REST call to the Cosmos DB is put together as a POST request between lines 32 to 60. The URL for the Cosmos DB is set in line 33. The header values are usually constants, only the time stamp (line 48) and the authorization string (line 51) are dynamic. Both values are only determined once every 5 minutes and then cached.

The query for the Cosmos DB was created in line 6 and formated as JSON in line 7. This query was set as body in the POST request in line 59.

The Cosmos DB Read-Only key is stored as named-value inside APIM. It was loaded in line 9 and stored in a variable. The variable is accessed in line 18 for the calculation of the authorisation header.

The authorization header is calculated in line 16 to 25 and stored in the variable authToken. Afterwards, the authToken is stored in the cache. It is important to configure the database and the container in line 20.