Quickstart: Connect a Go application to Azure Cosmos DB's API for MongoDB

Azure Cosmos DB is a multi-model database service that lets you quickly create and query document, table, key-value, and graph databases with global distribution and horizontal scale capabilities. In this quickstart, you create and manage an Azure Cosmos DB account by using the Azure Cloud Shell, clone an existing sample application from GitHub and configure it to work with Azure Cosmos DB.

The sample application is a command-line based todo management tool written in Go. Azure Cosmos DB's API for MongoDB is compatible with the MongoDB wire protocol, making it possible for any MongoDB client driver to connect to it. This application uses the Go driver for MongoDB in a way that is transparent to the application that the data is stored in an Azure Cosmos DB database.

Prerequisites

  • An Azure account with an active subscription. Create one for free. Or try Azure Cosmos DB for free without an Azure subscription. You can also use the Azure Cosmos DB Emulator with the connection string .mongodb://localhost:C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==@localhost:10255/admin?ssl=true.
  • Go installed on your computer, and a working knowledge of Go.
  • Git.
  • If you don't want to use Azure Cloud Shell, Azure CLI 2.0+.

Use Azure Cloud Shell

Azure hosts Azure Cloud Shell, an interactive shell environment that you can use through your browser. You can use either Bash or PowerShell with Cloud Shell to work with Azure services. You can use the Cloud Shell preinstalled commands to run the code in this article without having to install anything on your local environment.

To start Azure Cloud Shell:

Option Example/Link
Select Try It in the upper-right corner of a code block. Selecting Try It doesn't automatically copy the code to Cloud Shell. Example of Try It for Azure Cloud Shell
Go to https://shell.azure.com, or select the Launch Cloud Shell button to open Cloud Shell in your browser. Launch Cloud Shell in a new window
Select the Cloud Shell button on the menu bar at the upper right in the Azure portal. Cloud Shell button in the Azure portal

To run the code in this article in Azure Cloud Shell:

  1. Start Cloud Shell.

  2. Select the Copy button on a code block to copy the code.

  3. Paste the code into the Cloud Shell session by selecting Ctrl+Shift+V on Windows and Linux or by selecting Cmd+Shift+V on macOS.

  4. Select Enter to run the code.

Clone the sample application

Run the following commands to clone the sample repository.

  1. Open a command prompt, create a new folder named git-samples, then close the command prompt.

    mkdir "C:\git-samples"
    
  2. Open a git terminal window, such as git bash, and use the cd command to change to the new folder to install the sample app.

    cd "C:\git-samples"
    
  3. Run the following command to clone the sample repository. This command creates a copy of the sample app on your computer.

    git clone https://github.com/Azure-Samples/cosmosdb-go-mongodb-quickstart
    

Review the code

This step is optional. If you're interested in learning how the application works, you can review the following snippets. Otherwise, you can skip ahead to Run the application. The application layout is as follows:

.
├── go.mod
├── go.sum
└── todo.go

The following snippets are all taken from the todo.go file.

Connecting the Go app to Azure Cosmos DB

clientOptions encapsulates the connection string for Azure Cosmos DB, which is passed in using an environment variable (details in the upcoming section). The connection is initialized using mongo.NewClient to which the clientOptions instance is passed. Ping function is invoked to confirm successful connectivity (it is a fail-fast strategy)

    ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
    defer cancel()

    clientOptions := options.Client().ApplyURI(mongoDBConnectionString).SetDirect(true)
    
    c, err := mongo.NewClient(clientOptions)
    err = c.Connect(ctx)
    if err != nil {
        log.Fatalf("unable to initialize connection %v", err)
    }

    err = c.Ping(ctx, nil)
    if err != nil {
        log.Fatalf("unable to connect %v", err)
    }

Note

Using the SetDirect(true) configuration is important, without which you will get the following connectivity error: unable to connect connection(cdb-ms-prod-<azure-region>-cm1.documents.azure.com:10255[-4]) connection is closed

Create a todo item

To create a todo, we get a handle to a mongo.Collection and invoke the InsertOne function.

func create(desc string) {
    c := connect()
    ctx := context.Background()
    defer c.Disconnect(ctx)

    todoCollection := c.Database(database).Collection(collection)
    r, err := todoCollection.InsertOne(ctx, Todo{Description: desc, Status: statusPending})
    if err != nil {
        log.Fatalf("failed to add todo %v", err)
    }

We pass in a Todo struct that contains the description and the status (which is initially set to pending)

type Todo struct {
    ID          primitive.ObjectID `bson:"_id,omitempty"`
    Description string             `bson:"description"`
    Status      string             `bson:"status"`
}

List todo items

We can list TODOs based on criteria. A bson.D is created to encapsulate the filter criteria

func list(status string) {
    .....
    var filter interface{}
    switch status {
    case listAllCriteria:
        filter = bson.D{}
    case statusCompleted:
        filter = bson.D{{statusAttribute, statusCompleted}}
    case statusPending:
        filter = bson.D{{statusAttribute, statusPending}}
    default:
        log.Fatal("invalid criteria for listing todo(s)")
    }

Find is used to search for documents based on the filter and the result is converted into a slice of Todo

    todoCollection := c.Database(database).Collection(collection)
    rs, err := todoCollection.Find(ctx, filter)
    if err != nil {
        log.Fatalf("failed to list todo(s) %v", err)
    }
    var todos []Todo
    err = rs.All(ctx, &todos)
    if err != nil {
        log.Fatalf("failed to list todo(s) %v", err)
    }

Finally, the information is rendered in tabular format

    todoTable := [][]string{}

    for _, todo := range todos {
        s, _ := todo.ID.MarshalJSON()
        todoTable = append(todoTable, []string{string(s), todo.Description, todo.Status})
    }

    table := tablewriter.NewWriter(os.Stdout)
    table.SetHeader([]string{"ID", "Description", "Status"})

    for _, v := range todoTable {
        table.Append(v)
    }
    table.Render()

Update a todo item

A todo can be updated based on its _id. A bson.D filter is created based on the _id and another one is created for the updated information, which is a new status (completed or pending) in this case. Finally, the UpdateOne function is invoked with the filter and the updated document

func update(todoid, newStatus string) {
....
    todoCollection := c.Database(database).Collection(collection)
    oid, err := primitive.ObjectIDFromHex(todoid)
    if err != nil {
        log.Fatalf("failed to update todo %v", err)
    }
    filter := bson.D{{"_id", oid}}
    update := bson.D{{"$set", bson.D{{statusAttribute, newStatus}}}}
    _, err = todoCollection.UpdateOne(ctx, filter, update)
    if err != nil {
        log.Fatalf("failed to update todo %v", err)
    }

Delete a todo

A todo is deleted based on its _id and it is encapsulated in the form of a bson.D instance. DeleteOne is invoked to delete the document.

func delete(todoid string) {
....
    todoCollection := c.Database(database).Collection(collection)
    oid, err := primitive.ObjectIDFromHex(todoid)
    if err != nil {
        log.Fatalf("invalid todo ID %v", err)
    }
    filter := bson.D{{"_id", oid}}
    _, err = todoCollection.DeleteOne(ctx, filter)
    if err != nil {
        log.Fatalf("failed to delete todo %v", err)
    }
}

Build the application

Change into the directory where you cloned the application and build it (using go build).

cd monogdb-go-quickstart
go build -o todo

To confirm that the application was built properly.

./todo --help

Setup Azure Cosmos DB

Sign in to Azure

If you choose to install and use the CLI locally, this topic requires that you are running the Azure CLI version 2.0 or later. Run az --version to find the version. If you need to install or upgrade, see [Install Azure CLI].

If you are using an installed Azure CLI, sign in to your Azure subscription with the az login command and follow the on-screen directions. You can skip this step if you're using the Azure Cloud Shell.

az login 

Add the Azure Cosmos DB module

If you are using an installed Azure CLI, check to see if the cosmosdb component is already installed by running the az command. If cosmosdb is in the list of base commands, proceed to the next command. You can skip this step if you're using the Azure Cloud Shell.

If cosmosdb is not in the list of base commands, reinstall Azure CLI.

Create a resource group

Create a resource group with the az group create. An Azure resource group is a logical container into which Azure resources like web apps, databases and storage accounts are deployed and managed.

The following example creates a resource group in the West Europe region. Choose a unique name for the resource group.

If you are using Azure Cloud Shell, select Try It, follow the onscreen prompts to login, then copy the command into the command prompt.

az group create --name myResourceGroup --location "West Europe"

Create an Azure Cosmos DB account

Create a Cosmos account with the az cosmosdb create command.

In the following command, please substitute your own unique Cosmos account name where you see the <cosmosdb-name> placeholder. This unique name will be used as part of your Cosmos DB endpoint (https://<cosmosdb-name>.documents.azure.com/), so the name needs to be unique across all Cosmos accounts in Azure.

az cosmosdb create --name <cosmosdb-name> --resource-group myResourceGroup --kind MongoDB

The --kind MongoDB parameter enables MongoDB client connections.

When the Azure Cosmos DB account is created, the Azure CLI shows information similar to the following example.

Note

This example uses JSON as the Azure CLI output format, which is the default. To use another output format, see Output formats for Azure CLI commands.

{
  "databaseAccountOfferType": "Standard",
  "documentEndpoint": "https://<cosmosdb-name>.documents.azure.com:443/",
  "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.Document
DB/databaseAccounts/<cosmosdb-name>",
  "kind": "MongoDB",
  "location": "West Europe",
  "name": "<cosmosdb-name>",
  "readLocations": [
    {
      "documentEndpoint": "https://<cosmosdb-name>-westeurope.documents.azure.com:443/",
      "failoverPriority": 0,
      "id": "<cosmosdb-name>-westeurope",
      "locationName": "West Europe",
      "provisioningState": "Succeeded"
    }
  ],
  "resourceGroup": "myResourceGroup",
  "type": "Microsoft.DocumentDB/databaseAccounts",
  "writeLocations": [
    {
      "documentEndpoint": "https://<cosmosdb-name>-westeurope.documents.azure.com:443/",
      "failoverPriority": 0,
      "id": "<cosmosdb-name>-westeurope",
      "locationName": "West Europe",
      "provisioningState": "Succeeded"
    }
  ]
} 

Retrieve the database key

In order to connect to a Cosmos database, you need the database key. Use the az cosmosdb keys list command to retrieve the primary key.

az cosmosdb keys list --name <cosmosdb-name> --resource-group myResourceGroup --query "primaryMasterKey"

The Azure CLI outputs information similar to the following example.

"RUayjYjixJDWG5xTqIiXjC..."

Configure the application

Export the connection string, MongoDB database and collection names as environment variables.

export MONGODB_CONNECTION_STRING="mongodb://<COSMOSDB_ACCOUNT_NAME>:<COSMOSDB_PASSWORD>@<COSMOSDB_ACCOUNT_NAME>.mongo.cosmos.azure.com:10255/?ssl=true&replicaSet=globaldb&maxIdleTimeMS=120000&appName=@<COSMOSDB_ACCOUNT_NAME>@"

Note

The ssl=true option is important because of Cosmos DB requirements. For more information, see Connection string requirements.

For the MONGODB_CONNECTION_STRING environment variable, replace the placeholders for <COSMOSDB_ACCOUNT_NAME> and <COSMOSDB_PASSWORD>

  1. <COSMOSDB_ACCOUNT_NAME>: The name of the Azure Cosmos DB account you created
  2. <COSMOSDB_PASSWORD>: The database key extracted in the previous step
export MONGODB_DATABASE=todo-db
export MONGODB_COLLECTION=todos

You can choose your preferred values for MONGODB_DATABASE and MONGODB_COLLECTION or leave them as is.

Run the application

To create a todo

./todo --create "Create an Azure Cosmos DB database account"

If successful, you should see an output with the MongoDB _id of the newly created document:

added todo ObjectID("5e9fd6befd2f076d1f03bd8a")

Create another todo

./todo --create "Get the MongoDB connection string using the Azure CLI"

List all the todos

./todo --list all

You should see the ones you just added in a tabular format as such

+----------------------------+--------------------------------+-----------+
|             ID             |          DESCRIPTION           |  STATUS   |
+----------------------------+--------------------------------+-----------+
| "5e9fd6b1bcd2fa6bd267d4c4" | Create an Azure Cosmos DB      | pending   |
|                            | database account               |           |
| "5e9fd6befd2f076d1f03bd8a" | Get the MongoDB connection     | pending   |
|                            | string using the Azure CLI     |           |
+----------------------------+--------------------------------+-----------+

To update the status of a todo (e.g. change it to completed status), use the todo ID

./todo --update 5e9fd6b1bcd2fa6bd267d4c4,completed

List only the completed todos

./todo --list completed

You should see the one you just updated

+----------------------------+--------------------------------+-----------+
|             ID             |          DESCRIPTION           |  STATUS   |
+----------------------------+--------------------------------+-----------+
| "5e9fd6b1bcd2fa6bd267d4c4" | Create an Azure Cosmos DB      | completed |
|                            | database account               |           |
+----------------------------+--------------------------------+-----------+

View data in Data Explorer

Data stored in Azure Cosmos DB is available to view and query in the Azure portal.

To view, query, and work with the user data created in the previous step, login to the Azure portal in your web browser.

In the top Search box, enter Azure Cosmos DB. When your Cosmos account blade opens, select your Cosmos account. In the left navigation, select Data Explorer. Expand your collection in the Collections pane, and then you can view the documents in the collection, query the data, and even create and run stored procedures, triggers, and UDFs.

Data Explorer showing the newly created document

Delete a todo using it's ID

./todo --delete 5e9fd6b1bcd2fa6bd267d4c4,completed

List the todos to confirm

./todo --list all

The todo you just deleted should not be present

+----------------------------+--------------------------------+-----------+
|             ID             |          DESCRIPTION           |  STATUS   |
+----------------------------+--------------------------------+-----------+
| "5e9fd6befd2f076d1f03bd8a" | Get the MongoDB connection     | pending   |
|                            | string using the Azure CLI     |           |
+----------------------------+--------------------------------+-----------+

Clean up resources

When you're done with your app and Azure Cosmos DB account, you can delete the Azure resources you created so you don't incur more charges. To delete the resources:

  1. In the Azure portal Search bar, search for and select Resource groups.

  2. From the list, select the resource group you created for this quickstart.

    Select the resource group to delete

  3. On the resource group Overview page, select Delete resource group.

    Delete the resource group

  4. In the next window, enter the name of the resource group to delete, and then select Delete.

Next steps

In this quickstart, you learned how to create an Azure Cosmos DB MongoDB API account using the Azure Cloud Shell, and create and run a Go command-line app to manage todos. You can now import additional data to your Azure Cosmos DB account.