This tutorial shows you how build a simple CRUD Go application with CockroachDB and the GORM ORM.
For another use of GORM with CockroachDB, see our examples-orms repository.
Step 1. Start CockroachDB
Create a free cluster
Organizations without billing information on file can only create one CockroachDB Basic cluster.
- If you haven't already, sign up for a CockroachDB Cloud account.
- Log in to your CockroachDB Cloud account.
- On the Clusters page, click Create cluster.
- On the Select a plan page, select Basic.
- On the Cloud & Regions page, select a cloud provider (GCP or AWS) in the Cloud provider section.
- In the Regions section, select a region for the cluster. Refer to CockroachDB Cloud Regions for the regions where CockroachDB Basic clusters can be deployed. To create a multi-region cluster, click Add region and select additional regions.
- Click Next: Capacity.
- On the Capacity page, select Start for free. Click Next: Finalize.
- On the Finalize page, click Create cluster. - Your cluster will be created in a few seconds and the Create SQL user dialog will display. 
Set up your cluster connection
- Navigate to the cluster's Overview page, and select Connect. 
- Under the Connection String tab, download the cluster certificate. 
- Take note of the connection string provided. You'll use it to connect to the database later in this tutorial. 
- If you haven't already, download the CockroachDB binary.
- Run the - cockroach start-single-nodecommand:- $ cockroach start-single-node --advertise-addr 'localhost' --insecure- This starts an insecure, single-node cluster. 
- Take note of the following connection information in the SQL shell welcome text: - CockroachDB node starting at 2021-08-30 17:25:30.06524 +0000 UTC (took 4.3s) build: CCL v21.1.6 @ 2021/07/20 15:33:43 (go1.15.11) webui: http://localhost:8080 sql: postgresql://root@localhost:26257?sslmode=disable- You'll use the - sqlconnection string to connect to the cluster later in this tutorial.
The --insecure flag used in this tutorial is intended for non-production testing only. To run CockroachDB in production, use a secure cluster instead.
Step 2. Get the code
Clone the code's GitHub repo:
$ git clone https://github.com/cockroachlabs/example-app-go-gorm
The project has the following directory structure:
├── README.md
└── main.go
The main.go file defines an Account struct that maps to a new accounts table in the bank database. The file also contains some read and write database operations that are executed in the main method of the program.
package main
import (
    "context"
    "fmt"
    "log"
    "math/rand"
    "os"
    "time"
    "github.com/cockroachdb/cockroach-go/v2/crdb/crdbgorm"
    "github.com/google/uuid"
    "gorm.io/driver/postgres"
    "gorm.io/gorm"
)
// Account is our model, which corresponds to the "accounts" table
type Account struct {
    ID      uuid.UUID `gorm:"type:uuid;default:uuid_generate_v4()"`
    Balance int
}
// The `acctIDs` global variable tracks the random IDs generated by `addAccounts`
var acctIDs []uuid.UUID
// Insert new rows into the "accounts" table
// This function generates new UUIDs and random balances for each row, and
// then it appends the ID to the `acctIDs`, which other functions use to track the IDs
func addAccounts(db *gorm.DB, numRows int, transferAmount int) error {
    log.Printf("Creating %d new accounts...", numRows)
    for i := 0; i < numRows; i++ {
        newID := uuid.New()
        newBalance := rand.Intn(10000) + transferAmount
        if err := db.Create(&Account{ID: newID, Balance: newBalance}).Error; err != nil {
            return err
        }
        acctIDs = append(acctIDs, newID)
    }
    log.Println("Accounts created.")
    return nil
}
// Transfer funds between accounts
// This function adds `amount` to the "balance" column of the row with the "id" column matching `toID`,
// and removes `amount` from the "balance" column of the row with the "id" column matching `fromID`
func transferFunds(db *gorm.DB, fromID uuid.UUID, toID uuid.UUID, amount int) error {
    log.Printf("Transferring %d from account %s to account %s...", amount, fromID, toID)
    var fromAccount Account
    var toAccount Account
    db.First(&fromAccount, fromID)
    db.First(&toAccount, toID)
    if fromAccount.Balance < amount {
        return fmt.Errorf("account %s balance %d is lower than transfer amount %d", fromAccount.ID, fromAccount.Balance, amount)
    }
    fromAccount.Balance -= amount
    toAccount.Balance += amount
    if err := db.Save(&fromAccount).Error; err != nil {
        return err
    }
    if err := db.Save(&toAccount).Error; err != nil {
        return err
    }
    log.Println("Funds transferred.")
    return nil
}
// Print IDs and balances for all rows in "accounts" table
func printBalances(db *gorm.DB) {
    var accounts []Account
    db.Find(&accounts)
    fmt.Printf("Balance at '%s':\n", time.Now())
    for _, account := range accounts {
        fmt.Printf("%s %d\n", account.ID, account.Balance)
    }
}
// Delete all rows in "accounts" table inserted by `main` (i.e., tracked by `acctIDs`)
func deleteAccounts(db *gorm.DB, accountIDs []uuid.UUID) error {
    log.Println("Deleting accounts created...")
    err := db.Where("id IN ?", accountIDs).Delete(Account{}).Error
    if err != nil {
        return err
    }
    log.Println("Accounts deleted.")
    return nil
}
func main() {
    db, err := gorm.Open(postgres.Open(os.Getenv("DATABASE_URL")+"&application_name=$ docs_simplecrud_gorm"), &gorm.Config{})
    if err != nil {
        log.Fatal(err)
    }
    // Automatically create the "accounts" table based on the `Account`
    // model.
    db.AutoMigrate(&Account{})
    // The number of initial rows to insert
    const numAccts int = 5
    // The amount to be transferred between two accounts.
    const transferAmt int = 100
    // Insert `numAccts` rows into the "accounts" table.
    // To handle potential transaction retry errors, we wrap the call
    // to `addAccounts` in `crdbgorm.ExecuteTx`, a helper function for
    // GORM which implements a retry loop
    if err := crdbgorm.ExecuteTx(context.Background(), db, nil,
        func(tx *gorm.DB) error {
            return addAccounts(db, numAccts, transferAmt)
        },
    ); err != nil {
        // For information and reference documentation, see:
        //   https://www.cockroachlabs.com/docs/stable/error-handling-and-troubleshooting.html
        fmt.Println(err)
    }
    // Print balances before transfer.
    printBalances(db)
    // Select two account IDs
    fromID := acctIDs[0]
    toID := acctIDs[0:][rand.Intn(len(acctIDs))]
    // Transfer funds between accounts.  To handle potential
    // transaction retry errors, we wrap the call to `transferFunds`
    // in `crdbgorm.ExecuteTx`
    if err := crdbgorm.ExecuteTx(context.Background(), db, nil,
        func(tx *gorm.DB) error {
            return transferFunds(tx, fromID, toID, transferAmt)
        },
    ); err != nil {
        // For information and reference documentation, see:
        //   https://www.cockroachlabs.com/docs/stable/error-handling-and-troubleshooting.html
        fmt.Println(err)
    }
    // Print balances after transfer to ensure that it worked.
    printBalances(db)
    // Delete all accounts created by the earlier call to `addAccounts`
    // To handle potential transaction retry errors, we wrap the call
    // to `deleteAccounts` in `crdbgorm.ExecuteTx`
    if err := crdbgorm.ExecuteTx(context.Background(), db, nil,
        func(tx *gorm.DB) error {
            return deleteAccounts(db, acctIDs)
        },
    ); err != nil {
        // For information and reference documentation, see:
        //   https://www.cockroachlabs.com/docs/stable/error-handling-and-troubleshooting.html
        fmt.Println(err)
    }
}
CockroachDB may require the client to retry a transaction in the case of read/write contention. The CockroachDB Go client includes a generic retry function (ExecuteTx()) that runs inside a transaction and retries it as needed. The code sample shows how you can use this function to wrap SQL statements.
Step 3. Run the code
- Initialize the module: - $ go mod init basic-sample && go mod tidy
- Run the code: - $ go run main.go- The program will prompt you for a connection string to the database: - Enter a connection string:
- Enter the connection string to your running cluster. Tip:- postgresql://root@localhost:26257?sslmode=disableshould be the- sqlconnection URL provided in the- cockroachwelcome text.Tip:- Use the connection string provided in the Connection info window of the CockroachDB Cloud Console. Note:- You need to provide a SQL user password in order to securely connect to a CockroachDB Cloud cluster. The connection string should have a placeholder for the password ( - <ENTER-PASSWORD>).- The output should look similar to the following: - 2021/09/16 14:17:12 Creating 5 new accounts... 2021/09/16 14:17:12 Accounts created. Balance at '2021-09-16 14:17:12.68843 -0400 EDT m=+2.760587790': 1580d2f4-c9ec-4f26-bbe7-6a53e9aa5170 1947 26ddc77b-8068-409b-b305-0c5d873f7c43 7987 3d97ea5a-5108-4388-88e8-92524d5de5e8 4159 af49831d-d637-4a20-a9a7-01e9fe4628fe 8181 f0cc97ef-e3fe-4abb-a44a-0dd04207f7d4 2181 2021/09/16 14:17:12 Transferring 100 from account af49831d-d637-4a20-a9a7-01e9fe4628fe to account 3d97ea5a-5108-4388-88e8-92524d5de5e8... 2021/09/16 14:17:12 Funds transferred. Balance at '2021-09-16 14:17:12.759686 -0400 EDT m=+2.831841311': 1580d2f4-c9ec-4f26-bbe7-6a53e9aa5170 1947 26ddc77b-8068-409b-b305-0c5d873f7c43 7987 3d97ea5a-5108-4388-88e8-92524d5de5e8 4259 af49831d-d637-4a20-a9a7-01e9fe4628fe 8081 f0cc97ef-e3fe-4abb-a44a-0dd04207f7d4 2181 2021/09/16 14:17:12 Deleting accounts created... 2021/09/16 14:17:12 Accounts deleted.- The code runs a migration that creates the - accountstable in the- bankdatabase, based on the- Accountstruct defined at the top of the- main.gofile.- As shown in the output, the code also does the following: - Inserts some rows into the accountstable.
- Reads values from the table.
- Updates values in the table.
- Deletes values from the table.
 
- Inserts some rows into the 
What's next?
Read more about using the GORM ORM, or check out a more realistic implementation of GORM with CockroachDB in our examples-orms repository.
You might also be interested in the following pages: