> ## Documentation Index
> Fetch the complete documentation index at: https://cockroachlabs.mintlify.site/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:

<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 --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>

## 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/`

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:

```
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>
