---
title: "Create"
description: "Learn how to create Aerospike records using the Go client, covering key setup, write policies, and PutBins usage."
---

# Create

> For the complete documentation index see: [llms.txt](https://aerospike.com/docs/llms.txt)
> 
> All documentation pages available in markdown.

Jump to the [Code block](#code-block) for a combined complete example.

## Setup

The following examples will use the setup and record structure below to illustrate single record creation in an Aerospike database.

```go
import (

    "log"

    "github.com/aerospike/aerospike-client-go/v8"

    ast "github.com/aerospike/aerospike-client-go/v8/types"

)

// Establishes a connection to the server

client, err := aerospike.NewClient("127.0.0.1", 3000)

if err != nil {

    log.Fatal(err)

}

defer client.Close()
```

The record structure:

```asciidoc
Occurred: Integer

Reported: Integer

Posted: Integer

Report: Map

{

    shape: List,

    summary: String,

    city: String,

    state: String,

    duration: String

}

Location: GeoJSON
```

## Key

The [key](https://aerospike.com/docs/database/learn/architecture/data-storage/data-model#keys-and-digests) is a tuple made up of: `([namespace](https://aerospike.com/docs/database/learn/architecture/data-storage/data-model#namespaces), [set](https://aerospike.com/docs/database/learn/architecture/data-storage/data-model#sets), user-defined key)`.

The following example creates a key using the `sandbox` namespace, `ufodata` set, and user-defined key `5001`. The examples that follow reuse this `key`.

```go
// Create the record key

key, err := aerospike.NewKey("sandbox", "ufodata", 5001)

if err != nil {

    log.Fatal(err)

}
```

## Policies

Write policies define additional semantics for the write operation. You can set write policies on a per-command basis.

The following example creates a new write policy container object that sets the send key policy to `true`. This stores the user-defined key with the record, and returns it with read commands.

```go
// Create new write policy

policy := aerospike.NewWritePolicy(0, 0)

policy.SendKey = true
```

The following example creates a new write policy container object that sets the send key policy to `true`. This stores the user-defined key with the record, which increases per-record storage, and returns it with read commands. Leave it unset if your application reconstructs keys itself and does not need to read the original key back.

`PutBins` creates a record if it does not exist and merges the supplied bins if it does. The default `RecordExistsAction` is `UPDATE`. Bins that a later write does not name stay in the record.

| Action | Description |
| --- | --- |
| `UPDATE` | Create the record if it is missing. Merge bins if it exists. This is the default. |
| `UPDATE_ONLY` | Merge bins only if the record already exists. |
| `REPLACE` | Create the record if it is missing. Replace all bins if it exists. |
| `REPLACE_ONLY` | Replace all bins only if the record already exists. |
| `CREATE_ONLY` | Create the record only if it does not already exist. |

To reject the write when the record already exists, copy a write policy and set `CREATE_ONLY`. The server then returns `KEY_EXISTS_ERROR` instead of merging. Copy the policy rather than mutating a shared default so concurrent commands are unaffected.

```go
createPolicy := aerospike.NewWritePolicy(0, 0)

createPolicy.SendKey = true

createPolicy.RecordExistsAction = aerospike.CREATE_ONLY

err = client.PutBins(createPolicy, key, aerospike.NewBin("status", "new"))

if err != nil {

    if err.Matches(ast.KEY_EXISTS_ERROR) {

        log.Print("The record was already created.")

    } else {

        log.Fatal(err)

    }

}
```

`Matches` tests the server result code, which is what you need to distinguish outcomes such as `KEY_EXISTS_ERROR` from `GENERATION_ERROR`. The returned `Error` also works with `errors.Is` against the client’s sentinel errors, such as `aerospike.ErrKeyNotFound`. Use `Matches` for result codes and `errors.Is` for sentinels; see [Best practices](https://aerospike.com/docs/develop/client/go/best-practices#error-handling).

### Record expiration

The second argument to `NewWritePolicy` is the record time-to-live (TTL) in seconds. Leaving it at `0` does not mean “never expire.” It means “use the namespace [`default-ttl`](https://aerospike.com/docs/database/reference/config#namespace__default-ttl).” If that namespace has a finite `default-ttl`, records written with the default policy expire.

| Expiration | Meaning |
| --- | --- |
| `aerospike.TTLServerDefault` (`0`) | Use the namespace `default-ttl`. |
| `aerospike.TTLDontExpire` | Never expire. |
| `aerospike.TTLDontUpdate` | Leave the existing record’s TTL unchanged. |
| positive | TTL in seconds from now. |

```go
// Expire 60 seconds from now.

expiringPolicy := aerospike.NewWritePolicy(0, 60)
```

::: caution
The examples on this page, including the full program below, use `NewWritePolicy(0, 0)`. That inherits the namespace `default-ttl`, so records written with them expire wherever the namespace sets a finite `default-ttl`. Pass `aerospike.TTLDontExpire` if the records must persist indefinitely.
:::

### Concurrent writers

Each write increments the record generation. Without a generation check, the last write wins: a write overwrites the bins it names using whatever the record held at the time, and the writer whose value was replaced receives no error.

To refuse an update when another writer has already changed the record, set `GenerationPolicy` to `EXPECT_GEN_EQUAL` and pass the generation you last read. The server then returns `GENERATION_ERROR` instead of overwriting the other writer’s bins.

```go
record, err := client.Get(nil, key)

if err != nil {

    log.Fatal(err)

}

writePolicy := aerospike.NewWritePolicy(record.Generation, aerospike.TTLDontUpdate)

writePolicy.GenerationPolicy = aerospike.EXPECT_GEN_EQUAL

err = client.PutBins(writePolicy, key, aerospike.NewBin("status", "updated"))

if err != nil {

    if err.Matches(ast.GENERATION_ERROR) {

        log.Print("Another writer updated the record first.")

    } else {

        log.Fatal(err)

    }

}
```

### Write timeouts

A timed-out write may already have succeeded on the server. Check `err.IsInDoubt()` before retrying a non-idempotent create. Writes default to `MaxRetries = 0`, so the client does not retry them automatically.

```go
err = client.PutBins(createPolicy, key, aerospike.NewBin("status", "new"))

if err != nil {

    if err.IsInDoubt() {

        log.Print("Write timed out and may have succeeded.")

    } else {

        log.Fatal(err)

    }

}
```

Retrying an in-doubt `CREATE_ONLY` write cannot produce a duplicate, because the server still enforces create-only semantics on the retry. What the retry cannot tell you is _who_ created the record: a `KEY_EXISTS_ERROR` means either that your own in-doubt write committed, or that a different writer created the record in the meantime. Reading the record before retrying does not resolve this and introduces a race, because another writer can create it between the read and the retry.

Where that distinction matters, write a value that identifies the writer, such as a request ID bin, and read it back after a `KEY_EXISTS_ERROR` to determine whether the record is yours.

See [Update](https://aerospike.com/docs/develop/client/go/usage/atomic/update) for `UPDATE_ONLY`, `REPLACE`, and `REPLACE_ONLY`.

## Create a record

A [record](https://aerospike.com/docs/database/learn/architecture/data-storage/data-model#records) is the basic unit of storage in the database. A record is composed of: `([key](https://aerospike.com/docs/database/learn/architecture/data-storage/data-model#keys-and-digests), [metadata](https://aerospike.com/docs/database/learn/architecture/data-storage/data-model#metadata), [bins](https://aerospike.com/docs/database/learn/architecture/data-storage/data-model#bins-and-data-types))`.

### Setup

Expand the block below to see the creation of the variables used in the [write](#write) example below.

View the data creation

```go
// Create a list of shapes to add to the report map

shape := []string{"circle", "flash", "disc"}

// Create the report map

reportMap := map[string]interface{}{

    "city": "Ann Arbor",

    "state": "Michigan",

    "shape": shape,

    "duration": "5 minutes",

    "summary": "Large flying disc flashed in the sky above the student union. Craziest thing I've ever seen!"}

// Format coordinates as a GeoJSON string

geoLoc := "{\"type\":\"Point\", \"coordinates\":[42.2808,83.7430]}"
```

### Write

The following example shows the creation of the bins and the writing of the record to the database.

```go
// Create the bins as Bin("binName", value)

occurred := aerospike.NewBin("occurred", 20220531)

reported := aerospike.NewBin("reported", 20220601)

posted := aerospike.NewBin("posted", 20220601)

// reportMap defined in the section above

report := aerospike.NewBin("report", reportMap)

// geoLoc defined in the section above

location := aerospike.NewBin("location", aerospike.NewGeoJSONValue(geoLoc))

// Write the record to Aerospike

err = client.PutBins(policy, key, occurred, reported, posted, report, location)

if err != nil {

    log.Fatal(err)

}
```

## Code block

Expand this section for a single code block to create a record

```go
import (

    "log"

    "github.com/aerospike/aerospike-client-go/v8"

)

func main() {

    // Establishes a connection to the server

    client, err := aerospike.NewClient("127.0.0.1", 3000)

    if err != nil {

        log.Fatal(err)

    }

    defer client.Close()

    // Create new write policy.

    // TTL 0 inherits the namespace default-ttl; use aerospike.TTLDontExpire to never expire.

    policy := aerospike.NewWritePolicy(0, 0)

    policy.SendKey = true

    // Create the record key

    key, err := aerospike.NewKey("sandbox", "ufodata", 5001)

    if err != nil {

        log.Fatal(err)

    }

    // Create a list of shapes to add to the report map

    shape := []string{"circle", "flash", "disc"}

    // Create the report map

    reportMap := map[string]interface{}{

        "city": "Ann Arbor",

        "state": "Michigan",

        "shape": shape,

        "duration": "5 minutes",

        "summary": "Large flying disc flashed in the sky above the student union. Craziest thing I've ever seen!"}

    // Format coordinates as a GeoJSON string

    geoLoc := "{\"type\":\"Point\", \"coordinates\":[42.2808,83.7430]}"

    // Create the bins as Bin("binName", value)

    occurred := aerospike.NewBin("occurred", 20220531)

    reported := aerospike.NewBin("reported", 20220601)

    posted := aerospike.NewBin("posted", 20220601)

    // reportMap defined in the section above

    report := aerospike.NewBin("report", reportMap)

    // geoLoc defined in the section above

    location := aerospike.NewBin("location", aerospike.NewGeoJSONValue(geoLoc))

    // Write the record to Aerospike

    err = client.PutBins(policy, key, occurred, reported, posted, report, location)

    if err != nil {

        log.Fatal(err)

    }

}
```