> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cockroachlabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Build a Go App with CockroachDB and upper/db

export const InternalLink = ({version, path = "", children, ...props}) => {
  let detectedVersion = version || "stable";
  if (typeof window !== 'undefined' && !version) {
    const match = window.location.pathname.match(/\/docs\/([^/]+)/);
    if (match) {
      detectedVersion = match[1];
    }
  }
  const normalizedPath = path.startsWith("/") ? path.slice(1) : path;
  return <a href={`/docs/${detectedVersion}/${normalizedPath}`} {...props}>
      {children}
    </a>;
};

This tutorial shows you how build a simple Go application with CockroachDB and the [upper/db](https://upper.io/) data access layer.

## Before you begin

1. <InternalLink path="install-cockroachdb">Install CockroachDB</InternalLink>.
2. Start up a <InternalLink path="secure-a-cluster">secure</InternalLink> or <InternalLink path="start-a-local-cluster">insecure</InternalLink> local cluster.
3. Choose the instructions that correspond to whether your cluster is secure or insecure:

<Tabs>
  <Tab title="Secure">
    ## Step 1. Create the `maxroach` user and `bank` database

    Start the <InternalLink path="cockroach-sql">built-in SQL shell</InternalLink>:

    ```shell theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
    $ cockroach sql --certs-dir=certs
    ```

    In the SQL shell, issue the following statements to create the `maxroach` user and `bank` database:

    ```sql theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
    > CREATE USER IF NOT EXISTS maxroach;
    ```

    ```sql theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
    > CREATE DATABASE bank;
    ```

    Give the `maxroach` user the necessary permissions:

    ```sql theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
    > GRANT ALL ON DATABASE bank TO maxroach;
    ```

    Exit the SQL shell:

    ```sql theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
    > \q
    ```

    ## Step 2. Generate a certificate for the `maxroach` user

    Create a certificate and key for the `maxroach` user by running the following command:

    ```shell theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
    $ cockroach cert create-client maxroach --certs-dir=certs --ca-key=my-safe-directory/ca.key
    ```

    The code samples will run with `maxroach` as the user.

    ## Step 3. Run the Go code

    The sample code shown below uses upper/db to map Go-specific objects to SQL operations. Specifically, the code:

    * Creates the `accounts` table, if it does not already exist.
    * Deletes any existing rows in the `accounts` table.
    * Inserts two rows into the `accounts` table.
    * Prints the rows in the `accounts` table to the terminal.
    * Deletes the first row in the `accounts` table.
    * Updates the rows in the `accounts` table within an explicit <InternalLink path="transactions">transaction</InternalLink>.
    * Prints the rows in the `accounts` table to the terminal once more.

    ```go theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
    package main

        "fmt"
        "log"
        "time"

        "github.com/upper/db/v4"
        "github.com/upper/db/v4/adapter/cockroachdb"
    )

    // The settings variable stores connection details.
    var settings = cockroachdb.ConnectionURL{
        Host:     "localhost",
        Database: "bank",
        User:     "maxroach",
        Options: map[string]string{
            // Secure node.
             "sslrootcert": "certs/ca.crt",
             "sslkey":      "certs/client.maxroach.key",
             "sslcert":     "certs/client.maxroach.crt",
        },
    }

    // Accounts is a handy way to represent a collection.
    func Accounts(sess db.Session) db.Store {
        return sess.Collection("accounts")
    }

    // Account is used to represent a single record in the "accounts" table.
    type Account struct {
        ID      uint64 `db:"id,omitempty"`
        Balance int64  `db:"balance"`
    }

    // Collection is required in order to create a relation between the Account
    // struct and the "accounts" table.
    func (a *Account) Store(sess db.Session) db.Store {
        return Accounts(sess)
    }

    // createTables creates all the tables that are neccessary to run this example.
    func createTables(sess db.Session) error {
        _, err := sess.SQL().Exec(`
            CREATE TABLE IF NOT EXISTS accounts (
                ID SERIAL PRIMARY KEY,
                balance INT
            )
        `)
        if err != nil {
            return err
        }
        return nil
    }

    // crdbForceRetry can be used to simulate a transaction error and
    // demonstrate upper/db's ability to retry the transaction automatically.
    //
    // By default, upper/db will retry the transaction five times, if you want
    // to modify this number use: sess.SetMaxTransactionRetries(n).
    //
    // This is only used for demonstration purposes and not intended
    // for production code.
    func crdbForceRetry(sess db.Session) error {
        var err error

        // The first statement in a transaction can be retried transparently on the
        // server, so we need to add a placeholder statement so that our
        // force_retry() statement isn't the first one.
        _, err = sess.SQL().Exec(`SELECT 1`)
        if err != nil {
            return err
        }

        // If force_retry is called during the specified interval from the beginning
        // of the transaction it returns a retryable error. If not, 0 is returned
        // instead of an error.
        _, err = sess.SQL().Exec(`SELECT crdb_internal.force_retry('1s'::INTERVAL)`)
        if err != nil {
            return err
        }

        return nil
    }

    func main() {
        // Connect to the local CockroachDB node.
        sess, err := cockroachdb.Open(settings)
        if err != nil {
            log.Fatal("cockroachdb.Open: ", err)
        }
        defer sess.Close()

        // Adjust this number to fit your specific needs (set to 5, by default)
        // sess.SetMaxTransactionRetries(10)

        // Create the "accounts" table
        createTables(sess)

        // Delete all the previous items in the "accounts" table.
        err = Accounts(sess).Truncate()
        if err != nil {
            log.Fatal("Truncate: ", err)
        }

        // Create a new account with a balance of 1000.
        account1 := Account{Balance: 1000}
        err = Accounts(sess).InsertReturning(&account1)
        if err != nil {
            log.Fatal("sess.Save: ", err)
        }

        // Create a new account with a balance of 250.
        account2 := Account{Balance: 250}
        err = Accounts(sess).InsertReturning(&account2)
        if err != nil {
            log.Fatal("sess.Save: ", err)
        }

        // Printing records
        printRecords(sess)

        // Change the balance of the first account.
        account1.Balance = 500
        err = sess.Save(&account1)
        if err != nil {
            log.Fatal("sess.Save: ", err)
        }

        // Change the balance of the second account.
        account2.Balance = 999
        err = sess.Save(&account2)
        if err != nil {
            log.Fatal("sess.Save: ", err)
        }

        // Printing records
        printRecords(sess)

        // Delete the first record.
        err = sess.Delete(&account1)
        if err != nil {
            log.Fatal("Delete: ", err)
        }

        startTime := time.Now()

        // Add a couple of new records within a transaction.
        err = sess.Tx(func(tx db.Session) error {
            var err error

            if err = tx.Save(&Account{Balance: 887}); err != nil {
                return err
            }

            if time.Now().Sub(startTime) < time.Second*1 {
                // Will fail continuously for 2 seconds.
                if err = crdbForceRetry(tx); err != nil {
                    return err
                }
            }

            if err = tx.Save(&Account{Balance: 342}); err != nil {
                return err
            }

            return nil
        })
        if err != nil {
            log.Fatal("Could not commit transaction: ", err)
        }

        // Printing records
        printRecords(sess)
    }

    func printRecords(sess db.Session) {
        accounts := []Account{}
        err := Accounts(sess).Find().All(&accounts)
        if err != nil {
            log.Fatal("Find: ", err)
        }
        log.Printf("Balances:")
        for i := range accounts {
            fmt.Printf("\taccounts[%d]: %d\n", accounts[i].ID, accounts[i].Balance)
        }
    }
    ```

    Note that the sample code also includes a function that simulates a transaction error (`crdbForceRetry()`). Upper/db's CockroachDB adapter <InternalLink path="transaction-retry-error-reference#client-side-retry-handling">automatically retries transactions</InternalLink> when transaction errors are thrown. As a result, this function forces a transaction retry.

    To run the code, copy the sample above, or download it directly.

    <Tip>
      To clone a version of the code below that connects to insecure clusters, run the following command:

      `git clone https://github.com/cockroachlabs/hello-world-go-upperdb/`

      Note that you will need to edit the connection string to use the certificates that you generated when you set up your secure cluster.
    </Tip>
  </Tab>

  <Tab title="Insecure">
    <Danger>
      The `--insecure` flag used in this tutorial is intended for non-production testing only. To run CockroachDB in production, use a secure cluster instead.
    </Danger>

    ## Step 1. Create the `maxroach` user and `bank` database

    Start the <InternalLink path="cockroach-sql">built-in SQL shell</InternalLink>:

    ```shell theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
    $ cockroach sql --insecure
    ```

    In the SQL shell, issue the following statements to create the `maxroach` user and `bank` database:

    ```sql theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
    > CREATE USER IF NOT EXISTS maxroach;
    ```

    ```sql theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
    > CREATE DATABASE bank;
    ```

    Give the `maxroach` user the necessary permissions:

    ```sql theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
    > GRANT ALL ON DATABASE bank TO maxroach;
    ```

    Exit the SQL shell:

    ```sql theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
    > \q
    ```

    ## Step 2. Run the Go code

    The sample code shown below uses upper/db to map Go-specific objects to SQL operations. Specifically, the code:

    * Creates the `accounts` table, if it does not already exist.
    * Deletes any existing rows in the `accounts` table.
    * Inserts two rows into the `accounts` table.
    * Prints the rows in the `accounts` table to the terminal.
    * Deletes the first row in the `accounts` table.
    * Updates the rows in the `accounts` table within an explicit <InternalLink path="transactions">transaction</InternalLink>.
    * Prints the rows in the `accounts` table to the terminal once more.

    ```go theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
    package main

        "fmt"
        "log"
        "time"

        "github.com/upper/db/v4"
        "github.com/upper/db/v4/adapter/cockroachdb"
    )

    // The settings variable stores connection details.
    var settings = cockroachdb.ConnectionURL{
        Host:     "localhost",
        Database: "bank",
        User:     "maxroach",
        Options: map[string]string{
            // Insecure node.
            "sslmode": "disable",
        },
    }

    // Accounts is a handy way to represent a collection.
    func Accounts(sess db.Session) db.Store {
        return sess.Collection("accounts")
    }

    // Account is used to represent a single record in the "accounts" table.
    type Account struct {
        ID      uint64 `db:"id,omitempty"`
        Balance int64  `db:"balance"`
    }

    // Collection is required in order to create a relation between the Account
    // struct and the "accounts" table.
    func (a *Account) Store(sess db.Session) db.Store {
        return Accounts(sess)
    }

    // createTables creates all the tables that are neccessary to run this example.
    func createTables(sess db.Session) error {
        _, err := sess.SQL().Exec(`
            CREATE TABLE IF NOT EXISTS accounts (
                ID SERIAL PRIMARY KEY,
                balance INT
            )
        `)
        if err != nil {
            return err
        }
        return nil
    }

    // crdbForceRetry can be used to simulate a transaction error and
    // demonstrate upper/db's ability to retry the transaction automatically.
    //
    // By default, upper/db will retry the transaction five times, if you want
    // to modify this number use: sess.SetMaxTransactionRetries(n).
    //
    // This is only used for demonstration purposes and not intended
    // for production code.
    func crdbForceRetry(sess db.Session) error {
        var err error

        // The first statement in a transaction can be retried transparently on the
        // server, so we need to add a placeholder statement so that our
        // force_retry() statement isn't the first one.
        _, err = sess.SQL().Exec(`SELECT 1`)
        if err != nil {
            return err
        }

        // If force_retry is called during the specified interval from the beginning
        // of the transaction it returns a retryable error. If not, 0 is returned
        // instead of an error.
        _, err = sess.SQL().Exec(`SELECT crdb_internal.force_retry('1s'::INTERVAL)`)
        if err != nil {
            return err
        }

        return nil
    }

    func main() {
        // Connect to the local CockroachDB node.
        sess, err := cockroachdb.Open(settings)
        if err != nil {
            log.Fatal("cockroachdb.Open: ", err)
        }
        defer sess.Close()

        // Adjust this number to fit your specific needs (set to 5, by default)
        // sess.SetMaxTransactionRetries(10)

        // Create the "accounts" table
        createTables(sess)

        // Delete all the previous items in the "accounts" table.
        err = Accounts(sess).Truncate()
        if err != nil {
            log.Fatal("Truncate: ", err)
        }

        // Create a new account with a balance of 1000.
        account1 := Account{Balance: 1000}
        err = Accounts(sess).InsertReturning(&account1)
        if err != nil {
            log.Fatal("sess.Save: ", err)
        }

        // Create a new account with a balance of 250.
        account2 := Account{Balance: 250}
        err = Accounts(sess).InsertReturning(&account2)
        if err != nil {
            log.Fatal("sess.Save: ", err)
        }

        // Printing records
        printRecords(sess)

        // Change the balance of the first account.
        account1.Balance = 500
        err = sess.Save(&account1)
        if err != nil {
            log.Fatal("sess.Save: ", err)
        }

        // Change the balance of the second account.
        account2.Balance = 999
        err = sess.Save(&account2)
        if err != nil {
            log.Fatal("sess.Save: ", err)
        }

        // Printing records
        printRecords(sess)

        // Delete the first record.
        err = sess.Delete(&account1)
        if err != nil {
            log.Fatal("Delete: ", err)
        }

        startTime := time.Now()

        // Add a couple of new records within a transaction.
        err = sess.Tx(func(tx db.Session) error {
            var err error

            if err = tx.Save(&Account{Balance: 887}); err != nil {
                return err
            }

            if time.Now().Sub(startTime) < time.Second*1 {
                // Will fail continuously for 2 seconds.
                if err = crdbForceRetry(tx); err != nil {
                    return err
                }
            }

            if err = tx.Save(&Account{Balance: 342}); err != nil {
                return err
            }

            return nil
        })
        if err != nil {
            log.Fatal("Could not commit transaction: ", err)
        }

        // Printing records
        printRecords(sess)
    }

    func printRecords(sess db.Session) {
        accounts := []Account{}
        err := Accounts(sess).Find().All(&accounts)
        if err != nil {
            log.Fatal("Find: ", err)
        }
        log.Printf("Balances:")
        for i := range accounts {
            fmt.Printf("\taccounts[%d]: %d\n", accounts[i].ID, accounts[i].Balance)
        }
    }
    ```

    Note that the sample code also includes a function that simulates a transaction error (`crdbForceRetry()`). Upper/db's CockroachDB adapter <InternalLink path="transaction-retry-error-reference#client-side-retry-handling">automatically retries transactions</InternalLink> when transaction errors are thrown. As a result, this function forces a transaction retry.

    Copy the code or download it directly.

    To clone a version of the code below that connects to insecure clusters, run the following command:

    `git clone https://github.com/cockroachlabs/hello-world-go-upperdb/`
  </Tab>
</Tabs>

Change to the directory where you cloned the repo and get the dependencies with `go mod init`:

```shell theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
$ go mod init hello-world-go-upperdb
```

Then run the code:

```shell theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
$ go run main.go
```

The output should look similar to the following:

```text theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
go: finding module for package github.com/upper/db/v4
go: finding module for package github.com/upper/db/v4/adapter/cockroachdb
go: found github.com/upper/db/v4 in github.com/upper/db/v4 v4.0.0
2020/09/16 10:31:55 Balances:
    accounts[590467288222990337]: 1000
    accounts[590467288229576705]: 250
2020/09/16 10:31:55 Balances:
    accounts[590467288222990337]: 500
    accounts[590467288229576705]: 999
2020/09/16 10:31:55 upper/db: log_level=WARNING file=go/pkg/mod/github.com/upper/db/v4@v4.0.0/internal/sqladapter/session.go:642
    Session ID:     00006
    Transaction ID: 00005
    Query:          SELECT crdb_internal.force_retry('1ms'::INTERVAL)
    Error:          pq: restart transaction: crdb_internal.force_retry(): TransactionRetryWithProtoRefreshError: forced by crdb_internal.force_retry()
    Time taken:     0.00171s
    Context:        context.Background

2020/09/16 10:31:55 upper/db: log_level=WARNING file=go/pkg/mod/github.com/upper/db/v4@v4.0.0/internal/sqladapter/session.go:642
    Session ID:     00006
    Transaction ID: 00005
    Query:          INSERT INTO "accounts" ("balance") VALUES ($1) RETURNING "id"
    Arguments:      []interface {}{887}
    Error:          pq: current transaction is aborted, commands ignored until end of transaction block
    Time taken:     0.00065s
    Context:        context.Background

2020/09/16 10:31:56 Balances:
    accounts[590467288229576705]: 999
    accounts[590467288342757377]: 887
    accounts[590467288350064641]: 342
```

Note that the forced transaction errors result in errors printed to the terminal, but the transactions are retried until they succeed.

## What's next?

Read more about upper/db:

* [Introduction to upper/db](https://upper.io/v4/getting-started)
* [The upper/db tour](https://tour.upper.io/)
* [upper/db reference docs](https://pkg.go.dev/github.com/upper/db/v4)

You might also be interested in the following pages:

* <InternalLink path="connection-parameters">Client Connection Parameters</InternalLink>
* <InternalLink path="connection-pooling">Connection Pooling</InternalLink>
* <InternalLink path="demo-replication-and-rebalancing">Data Replication</InternalLink>
* <InternalLink path="demo-cockroachdb-resilience">CockroachDB Resilience</InternalLink>
* <InternalLink path="demo-replication-and-rebalancing">Replication & Rebalancing</InternalLink>
* <InternalLink path="demo-automatic-cloud-migration">Cross-Cloud Migration</InternalLink>
* <InternalLink path="orchestrate-a-local-cluster-with-kubernetes-insecure">Automated Operations</InternalLink>
