Building Event-Driven Go applications with Azure Cosmos DB and Azure Functions

The Go programming language is a great fit for building serverless applications. Go applications can be easily compiled to a single, statically linked binary, making deployment simple and reducing external dependencies. They start up quickly, which is ideal for serverless environments where functions are frequently invoked from a cold start. Go applications also tend to use less memory compared to other languages, helping optimize resource usage and reduce costs in serverless scenarios. Azure Functions supports Go using custom handlers, and you can use triggers and input and output bindings via extension bundles. Azure Functions is tightly integrated with Azure Cosmos DB using bindings (input, output), and triggers. This blog post will walk you through how to build Azure Functions with Go that make use of these Azure Cosmos DB integrations. Bindings allow you to easily read and write data to Cosmos DB, while triggers are useful for building event-driven applications that respond to changes in your data in Cosmos DB. Part 1 of this blog starts off with a function that gets triggered by changes in a Cosmos DB container and simply logs the raw Azure Functions event payload and the Cosmos DB document. You will learn how to run the function and also test it with Cosmos DB locally, thanks to the Cosmos DB emulator and Azure Functions Core Tools. If this is your first time working with Go and Azure Functions, you should find it helpful to get up and running quickly. Although you can deploy it to Azure, we will save that for the next part of this blog. Part 2 dives into another function that generates embeddings for the documents in the Cosmos DB container. This example will use an Azure OpenAI embedding model to generate embeddings for the documents in the container and then store the embeddings back in the container. This is useful for building applications that require semantic search or other generative AI applications. Check out the GitHub repository for the complete code. Part 1: Build a simple Cosmos DB trigger-based function and run it locally Just as the Cosmos DB emulator lets you run Cosmos DB locally, Azure Functions Core Tools lets you develop and test your functions locally. Start by installing the Azure Functions Core Tools – refer to the documentation for instructions for your OS. For example, on Linux, you can: sudo apt-get update sudo apt-get install azure-functions-core-tools-4 Next, start the Cosmos DB emulator. The commands below are for Linux and use the Docker container-based approach - refer to the documentation for other options. You need to have Docker installed and running on your machine. If you don't have it installed, please refer to the Docker installation guide. docker pull mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest docker run \ --publish 8081:8081 \ --name linux-emulator \ -e AZURE_COSMOS_EMULATOR_PARTITION_COUNT=1 \ mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest Make sure to configure the emulator SSL certificate as well. For example, for the Linux system I was using, I ran the following command to download the certificate and regenerate the certificate bundle: curl --insecure https://localhost:8081/_explorer/emulator.pem > ~/emulatorcert.crt sudo update-ca-certificates Use the following URL to navigate to the Cosmos DB Data Explorer using your browser: http://localhost:8081/_explorer/index.html. Create the following resources: A database A container with partition key /id – this is the source container A lease container with the name leases and partition key /id – it is used by the trigger to keep track of the changes in the source container. Clone the GitHub repository with the code for the function: git clone https://github.com/abhirockzz/golang_cosmosdb_azure_functions.git cd golang_cosmosdb_azure_functions/getting_started_guide Create a local.settings.json file with the Cosmos DB related info. Use the same database and container names as you created in the previous step. The local.settings.json file is used to store the configuration settings for your function app when running locally: { "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "", "FUNCTIONS_WORKER_RUNTIME": "custom", "COSMOS_CONNECTION": "AccountEndpoint=https://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==;", "COSMOS_DATABASE_NAME": "test", "COSMOS_CONTAINER_NAME": "tasks" } } COSMOS_CONNECTION has a static value for the connection string for the Cosmos DB emulator – do not change it. Build the Go function binary using the following command. This will create a binary file named main in the current directory: go build -o main main.go Start the function locally: func start This will start the function app and listen for incoming requests. You should see output similar to this: [2025-

Apr 25, 2025 - 14:26
 0
Building Event-Driven Go applications with Azure Cosmos DB and Azure Functions

The Go programming language is a great fit for building serverless applications. Go applications can be easily compiled to a single, statically linked binary, making deployment simple and reducing external dependencies. They start up quickly, which is ideal for serverless environments where functions are frequently invoked from a cold start. Go applications also tend to use less memory compared to other languages, helping optimize resource usage and reduce costs in serverless scenarios.

Azure Functions supports Go using custom handlers, and you can use triggers and input and output bindings via extension bundles. Azure Functions is tightly integrated with Azure Cosmos DB using bindings (input, output), and triggers.

This blog post will walk you through how to build Azure Functions with Go that make use of these Azure Cosmos DB integrations. Bindings allow you to easily read and write data to Cosmos DB, while triggers are useful for building event-driven applications that respond to changes in your data in Cosmos DB.

Part 1 of this blog starts off with a function that gets triggered by changes in a Cosmos DB container and simply logs the raw Azure Functions event payload and the Cosmos DB document. You will learn how to run the function and also test it with Cosmos DB locally, thanks to the Cosmos DB emulator and Azure Functions Core Tools. If this is your first time working with Go and Azure Functions, you should find it helpful to get up and running quickly. Although you can deploy it to Azure, we will save that for the next part of this blog.

Part 2 dives into another function that generates embeddings for the documents in the Cosmos DB container. This example will use an Azure OpenAI embedding model to generate embeddings for the documents in the container and then store the embeddings back in the container. This is useful for building applications that require semantic search or other generative AI applications.

Check out the GitHub repository for the complete code.

Part 1: Build a simple Cosmos DB trigger-based function and run it locally

Just as the Cosmos DB emulator lets you run Cosmos DB locally, Azure Functions Core Tools lets you develop and test your functions locally.

Start by installing the Azure Functions Core Tools – refer to the documentation for instructions for your OS. For example, on Linux, you can:

sudo apt-get update
sudo apt-get install azure-functions-core-tools-4

Next, start the Cosmos DB emulator. The commands below are for Linux and use the Docker container-based approach - refer to the documentation for other options.

You need to have Docker installed and running on your machine. If you don't have it installed, please refer to the Docker installation guide.

docker pull mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest

docker run \
 --publish 8081:8081 \
 --name linux-emulator \
 -e AZURE_COSMOS_EMULATOR_PARTITION_COUNT=1 \
 mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest

Make sure to configure the emulator SSL certificate as well. For example, for the Linux system I was using, I ran the following command to download the certificate and regenerate the certificate bundle:

curl --insecure https://localhost:8081/_explorer/emulator.pem > ~/emulatorcert.crt
sudo update-ca-certificates

Use the following URL to navigate to the Cosmos DB Data Explorer using your browser: http://localhost:8081/_explorer/index.html. Create the following resources:

  • A database
  • A container with partition key /id – this is the source container
  • A lease container with the name leases and partition key /id – it is used by the trigger to keep track of the changes in the source container.

Clone the GitHub repository with the code for the function:

git clone https://github.com/abhirockzz/golang_cosmosdb_azure_functions.git
cd golang_cosmosdb_azure_functions/getting_started_guide

Create a local.settings.json file with the Cosmos DB related info. Use the same database and container names as you created in the previous step. The local.settings.json file is used to store the configuration settings for your function app when running locally:

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "",
    "FUNCTIONS_WORKER_RUNTIME": "custom",
    "COSMOS_CONNECTION": "AccountEndpoint=https://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==;",
    "COSMOS_DATABASE_NAME": "test",
    "COSMOS_CONTAINER_NAME": "tasks"
  }
}

COSMOS_CONNECTION has a static value for the connection string for the Cosmos DB emulator – do not change it.

Build the Go function binary using the following command. This will create a binary file named main in the current directory:

go build -o main main.go

Start the function locally:

func start

This will start the function app and listen for incoming requests. You should see output similar to this:

[2025-04-25T07:44:53.921Z] Worker process started and initialized.

Functions:

        processor: cosmosDBTrigger

For detailed output, run func with --verbose flag.
[2025-04-25T07:44:58.809Z] Host lock lease acquired by instance ID '0000000000000000000000006ADD8D3E'.

//...

Add data to the source container in Cosmos DB. You can do this by navigating to Data Explorer in the emulator. For example, add a document with the following JSON:

{
  "id": "42",
  "description": "test"
}

The function should be triggered automatically when the document is added to the container. You can check the logs of the function app to see if it was triggered successfully:

[2025-04-25T07:48:10.559Z] Executing 'Functions.processor' (Reason='New changes on container tasks at 2025-04-25T07:48:10.5593689Z', Id=7b62f8cf-683b-4a5b-9db0-83d049bc4c86)
[2025-04-25T07:48:10.565Z] processor function invoked...
[2025-04-25T07:48:10.565Z] Raw event payload: {{"[{\"id\":\"42\",\"description\":\"test\",\"_rid\":\"AxI2AL1rrFoDAAAAAAAAAA==\",\"_self\":\"dbs/AxI2AA==/colls/AxI2AL1rrFo=/docs/AxI2AL1rrFoDAAAAAAAAAA==/\",\"_etag\":\"\\\"00000000-0000-0000-b5b6-6123f4d401db\\\"\",\"_attachments\":\"attachments/\",\"_ts\":1745567285,\"_lsn\":4}]"}} {{processor 2025-04-25T07:48:10.560243Z 4f29b3f3-ba95-4043-9b67-2856a43b4734}}}
[2025-04-25T07:48:10.566Z] Cosmos DB document: {42  AxI2AL1rrFoDAAAAAAAAAA== dbs/AxI2AA==/colls/AxI2AL1rrFo=/docs/AxI2AL1rrFoDAAAAAAAAAA==/ "00000000-0000-0000-b5b6-6123f4d401db" attachments/ 1745567285 4}
[2025-04-25T07:48:10.566Z] Executed 'Functions.processor' (Succeeded, Id=7b62f8cf-683b-4a5b-9db0-83d049bc4c86, Duration=6ms)

//.....

How it works

Here is a very high-level overview of the code:

  • main.go – Implements an HTTP server with a processor endpoint. When triggered, it reads a Cosmos DB trigger payload from the request, parses the nested documents, logs information, and returns a structured JSON response. It uses types and helpers from the common package.

  • common package: Contains shared types and utilities for Cosmos DB trigger processing:

    • payload.go: Defines data structures for the trigger payload, documents, and response.
    • parse.go: Provides a Parse function to extract and unmarshal documents from the trigger payload’s nested JSON structure.

Part 2: Use Azure OpenAI to generate embeddings for the documents in the Cosmos DB container

In addition to its low-latency, high-performance, and scalability characteristics, its support for Vector (semantic/similarity), Full-text, and Hybrid search makes Azure Cosmos DB a great fit for generative AI applications.

Consider a use case for managing a product catalog for an e-commerce platform. Each time a new product is added to the system (with a short description like “Bluetooth headphones with noise cancellation”), we want to immediately make that item searchable semantically. As soon as the product document is written to Cosmos DB, an Azure Function is triggered. It extracts the product description, generates a vector embedding using Azure OpenAI, and writes the embedding back to the same document using an output binding. With the embedding in place, the product is now indexed and ready for semantic and hybrid search queries, without any additional effort.

Image description

Prerequisites

You will run this example in Azure, so you need to have an Azure account. If you don't have one, you can create a free account.

Create an Azure Cosmos DB for NoSQL account. Enable the vector indexing and search feature – this is a one-time operation.

Just like before, you will need to create the following resources:

  • A database
  • A container with partition key /id – this is the source container
  • A lease container with the name leases and partition key /id – it is used by the trigger to keep track of the changes in the source container.

The lease container needs to be created in advance since we have configured Azure Functions to use managed identity to access the Cosmos DB account – you don't need to use keys or connection strings.

Create an Azure OpenAI Service resource. Azure OpenAI Service provides access to OpenAI's models including GPT-4o, GPT-4o mini (and more), as well as embedding models. Deploy an embedding model of your choice using the Azure AI Foundry portal (for example, I used the text-embedding-3-small model). Just like the Cosmos DB account, the Azure Function app uses a managed identity to access the Azure OpenAI Service resource.

Deploy resources

Move into the right directory:

cd ../embeddings_generator

To simplify the deployment of the function app along with the required resources and configuration, you can use the deploy.sh script. At a high level, it:

  • Sets up environment variables for Azure resources.
  • Creates an Azure resource group, storage account, and function app plan.
  • Deploys a custom Go-based Azure Function App.
  • Builds the Go binary for Windows.
  • Publishes the function app to Azure.
  • Enables the function app system identity and provides it the required roles for Cosmos DB and Azure OpenAI resource access.

Before you deploy the solution, update the local.settings.json. Use the same database and container names as you created in the previous step:

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "",
    "FUNCTIONS_WORKER_RUNTIME": "custom",
    "COSMOS_CONNECTION__accountEndpoint": "https://ENTER_COSMOSDB_ACCOUNT_NAME.documents.azure.com:443/",
    "COSMOS_DATABASE_NAME": "name of the database",
    "COSMOS_CONTAINER_NAME": "name of the container",
    "COSMOS_HASH_PROPERTY": "hash",
    "COSMOS_VECTOR_PROPERTY": "embedding",
    "COSMOS_PROPERTY_TO_EMBED": "description",
    "OPENAI_DEPLOYMENT_NAME": "enter the embedding model deployment name e.g. text-embedding-3-small",
    "OPENAI_DIMENSIONS": "enter the dimensions e.g. 1536",
    "OPENAI_ENDPOINT": "https://ENTER_OPENAI_RESOURCE_NAME.openai.azure.com/"
  }
}
  • COSMOS_CONNECTION_accountEndpoint: Endpoint URL for the Azure Cosmos DB account.
  • COSMOS_DATABASE_NAME: Name of the Cosmos DB database to use.
  • COSMOS_CONTAINER_NAME: Name of the Cosmos DB container to use.
  • COSMOS_HASH_PROPERTY: Name of the property used as a hash in Cosmos DB documents (no need to modify this).
  • COSMOS_VECTOR_PROPERTY: Name of the property storing vector embeddings in Cosmos DB.
  • COSMOS_PROPERTY_TO_EMBED: Name of the property whose value will be embedded. Change this based on your document structure.
  • OPENAI_DEPLOYMENT_NAME: Name of the Azure OpenAI model deployment to use for embeddings.
  • OPENAI_DIMENSIONS: Number of dimensions for the embedding vectors.
  • OPENAI_ENDPOINT: Endpoint URL for the Azure OpenAI resource.

Run the deploy.sh script:

chmod +x deploy.sh
./deploy.sh

As part of the azure functionapp publish command that's used in the script, you will be prompted to overwrite the value of the existing AzureWebJobsStorage setting in the local.settings.json file to Azure – choose "no".

Run the end-to-end example

Add data to the source container in Cosmos DB. For example, add a document with the following JSON:

{
  "id": "de001c6d-4efe-4a65-a59a-39a0580bfa2a",
  "description": "Research new technology"
}

The function should be triggered automatically when the document is added to the container. You can check the logs of the function app to see if it was triggered successfully:

func azure functionapp logstream 

You should see logs similar to this (the payload will be different depending on the data you add):

2025-04-23T05:34:41Z [Information] function invoked
2025-04-23T05:34:41Z [Information] cosmosVectorPropertyName: embedding
2025-04-23T05:34:41Z [Information] cosmosVectorPropertyToEmbedName: description
2025-04-23T05:34:41Z [Information] cosmosHashPropertyName: hash
2025-04-23T05:34:41Z [Information] Processing 1 documents
2025-04-23T05:34:41Z [Information] Processing document ID: de001c6d-4efe-4a65-a59a-39a0580bfa2a
2025-04-23T05:34:41Z [Information] Document data: Research new technology
2025-04-23T05:34:41Z [Information] New document detected, generated hash: 5bb57053273563e2fbd4202c666373ccd48f86eaf9198d7927a93a555aa200aa
2025-04-23T05:34:41Z [Information] Document modification status: true, hash: 5bb57053273563e2fbd4202c666373ccd48f86eaf9198d7927a93a555aa200aa
2025-04-23T05:34:41Z [Information] Created embedding for document: map[description:Research new technology id:de001c6d-4efe-4a65-a59a-39a0580bfa2a]
2025-04-23T05:34:41Z [Information] Adding 1 document with embeddings
2025-04-23T05:34:41Z [Information] Added enriched documents to binding output
2025-04-23T05:34:41Z [Information] Executed 'Functions.cosmosdbprocessor' (Succeeded, Id=91f4760f-047a-4867-9030-46a6602ab179, Duration=128ms)

//....

Verify the data in Cosmos DB. You should see an embedding for the description property of the document stored in the embedding property. It should look something like this:

{
  "id": "de001c6d-4efe-4a65-a59a-39a0580bfa2a",
  "description": "Research new technology",
  "embedding": [
    0.028226057, -0.00958694
    //....
  ],
  "hash": "5bb57053273563e2fbd4202c666373ccd48f86eaf9198d7927a93a555aa200aa"
}

Once the embeddings are generated, you can integrate this with generative AI applications. For example, you can use the Vector Search feature of Azure Cosmos DB to perform similarity searches based on the embeddings.

How it works

Here is a very high-level overview of the code:

  • main.go: Implements an HTTP server with a cosmosdbprocessor endpoint. When triggered, it reads a Cosmos DB trigger payload from the request, parses the nested documents, generates embeddings using Azure OpenAI, and writes the enriched documents back to the Cosmos DB container.
    • Exposes the cosmosdbprocessor endpoint, which processes incoming Cosmos DB documents.
    • For each document, checks if it is new or modified (using a hash), generates an embedding (vector) using Azure OpenAI, and prepares enriched documents for output.
    • Handles logging and error reporting for the function execution.
  • common package: Contains shared utilities and types for processing Cosmos DB documents
    • embedding.go: Handles creation of embeddings using Azure OpenAI.
    • parse.go: Parses and extracts documents from the Cosmos DB trigger payload.
    • payload.go: Defines data structures for payloads and responses used across the project.

The function uses a hash property to check if the document has already been processed. If the hash value is different from the one stored in Cosmos DB, it means that the document has been modified and needs to be re-processed. In this case, the function will generate a new embedding and update the document with the new hash value. This ensures that the function does not get stuck in an infinite loop. If the hash value is the same, it means that the document has not been modified and does not need to be re-processed. In this case, the function will log that the document is unchanged and will not generate a new embedding.

You should see logs similar to this:

2025-04-23T05:34:42Z   [Information]   function invoked
2025-04-23T05:34:42Z   [Information]   cosmosVectorPropertyName: embedding
2025-04-23T05:34:42Z   [Information]   cosmosVectorPropertyToEmbedName: description
2025-04-23T05:34:42Z   [Information]   cosmosHashPropertyName: hash
2025-04-23T05:34:42Z   [Information]   Processing 1 document
2025-04-23T05:34:42Z   [Information]   Processing document ID: de001c6d-4efe-4a65-a59a-39a0580bfa2a
2025-04-23T05:34:42Z   [Information]   Document data: Research new technology
2025-04-23T05:34:42Z   [Information]   Document unchanged, hash: 5bb57053273563e2fbd4202c666373ccd48f86eaf9198d7927a93a555aa200aa
2025-04-23T05:34:42Z   [Information]   Document modification status: false, hash:
2025-04-23T05:34:42Z   [Information]   Executed 'Functions.cosmosdbprocessor' (Succeeded, Id=f0cf039a-5de5-4cc1-b29d-928ce32b294e, Duration=6ms)

//....

Delete resources

Be sure to clean up the resources you created in Azure. You can do this using the Azure portal or the Azure CLI. For example, to delete the resource group and all its resources, run:

az group delete --name 

This will delete the resource group and all its resources, including the Cosmos DB account, function app, and storage account.

Conclusion

In this blog post, you learned how to build Azure Functions with Go that use Cosmos DB triggers and bindings. You started with a simple function that logs the raw event payload and the Cosmos DB document, and then moved on to a more complex function that generates embeddings for the documents in the Cosmos DB container using Azure OpenAI. You also learned how to run the functions locally using the Cosmos DB emulator and Azure Functions Core Tools, and how to deploy them to Azure.

You can use these examples as a starting point for building your own serverless applications with Go and Azure Functions. The combination of Go's performance and simplicity, along with Azure Functions' scalability and integration with Cosmos DB, makes it a powerful platform for building modern applications.