---
title: "Create"
description: "Learn how to create and update Aerospike records using the Rust client's put() method, bins, and write policies."
---

# Create

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

This guide provides practical examples of using the Aerospike Rust client’s `put()` method to write single or multiple bins to a record. It covers partial bin updates without read-modify-write, bin deletion, `RecordExistsAction::CreateOnly` inserts, customizing write behavior with `WritePolicy`, managing record TTLs, and optimistic locking using read-modify-write patterns.

The Aerospike Rust client’s `put()` method writes records to the database. Note that if they do not already exist, the set and bins are automatically created. You don’t have to define a schema for the database.

The following examples adds a bin `mybin` in the record `mykey` with a string value `myvalue`. This example uses the namespace `test`, which is the default namespace specified in the server configuration file. The following write policy specifies the re-transmission policy, timeout interval, record expiration, and what to do if the record already exists.

```rust
let mut policy = WritePolicy::default();

policy.base_policy.total_timeout = 50;  // 50 millisecond timeout.
```

> 📖 **API reference**: [`WritePolicy::default`](https://docs.rs/aerospike/latest/aerospike/struct.WritePolicy.html)

## Writing single value

When writing records, you must identify the record in the database using a key.

The following example creates a key for the record to insert. The value used for the `key` is the string `mykey`, to be stored in the Namespace `test` in the set `myset`. You can use other data types, such as integer for `key`.

```rust
let key = as_key!("test", "myset", "mykey");

let bin = as_bin!("mybin", "myvalue");

match client.put(&policy, &key, &[bin]).await {

    Ok(()) => println!("Record written"),

    Err(err) => println!("Error writing record: {}", err),

}
```

> 📖 **API reference**: [`Client::put`](https://docs.rs/aerospike/latest/aerospike/struct.Client.html#method.put)

`Client::put` expects a slice of bins (`&[Bin]`), not a slice of references. Pass `&[bin]` for a single bin or `&[bin1, bin2]` for multiple bins.

## Updating a single bin

To update a single bin without changing others, call `put()` with only that bin. The server updates it in place; you do not need to read the record first. Use [read-modify-write](#read-modify-write) only if you require generation-based optimistic locking.

```rust
let key = as_key!("test", "users", "user:1");

let active = as_bin!("active", false);

match client.put(&WritePolicy::default(), &key, &[active]).await {

    Ok(()) => println!("Updated active bin"),

    Err(err) => println!("Error updating record: {}", err),

}
```

To update a string bin the same way:

```rust
let key = as_key!("test", "users", "user:1");

let name = as_bin!("name", "Alice Smith");

client.put(&WritePolicy::default(), &key, &[name]).await?;
```

> 📖 **API reference**: [`Client::put`](https://docs.rs/aerospike/latest/aerospike/struct.Client.html#method.put) | [`WritePolicy::default`](https://docs.rs/aerospike/latest/aerospike/struct.WritePolicy.html)

::: note
This updates `active` in place and leaves every other bin on the record unchanged, including `age` or any other bin on the same record. To overwrite the record with only the bins you specify, set `WritePolicy.record_exists_action` to `RecordExistsAction::ReplaceOnly` and supply the full bin set. See [Modifying write behavior](#modifying-write-behavior) below.
:::

## Writing multiple values

To update multiple bins in a record, create additional bins and add them to the `put()` call:

```rust
// Write multiple values.

let bin1 = as_bin!("name", "John");

let bin2 = as_bin!("age", 25);

match client.put(&policy, &key, &[bin1, bin2]).await {

    Ok(()) => println!("All records written"),

    Err(err) => println!("Error writing records: {}", err),

};
```

> 📖 **API reference**: [`Client::put`](https://docs.rs/aerospike/latest/aerospike/struct.Client.html#method.put)

## Deleting a bin

To delete a bin, set the bin value to the empty value:

```rust
// Delete the "age" bin by writing a nil value.

let bin1 = as_bin!("age", None);

match client.put(&policy, &key, &[bin1]).await {

    Ok(()) => println!("Bin 'age' deleted"),

    Err(err) => println!("Error deleting bin: {}", err),

};
```

> 📖 **API reference**: [`Client::put`](https://docs.rs/aerospike/latest/aerospike/struct.Client.html#method.put)

## Modifying write behavior

The default behavior of a `put()` operation is:

-   Create the record (if it doesn’t exist) in the cluster.
-   Add the bins if they do not exist.
-   Update bin values in the record (if the bins already exist).
-   If the record has other bins, keep them in the record.

Use the `WritePolicy` to change write behavior. Some policy changes are:

-   Only write if the record does not already exist:
    -   Set `policy.record_exists_action` to `RecordExistsAction::CreateOnly`.
-   Completely replace a record only if it exists:
    -   Set `policy.record_exists_action` to `RecordExistsAction::ReplaceOnly`

### Create-only writes

To insert a record only when the key does not exist, set `RecordExistsAction::CreateOnly` on `WritePolicy`. The server returns an error if the record is already present. Do not call `exists()` or `get()` first; the policy enforces create-only semantics.

```rust
use aerospike::{

    as_bin, as_key,

    policy::RecordExistsAction,

    Error, ResultCode, WritePolicy,

};

let key = as_key!("test", "users", "user:99");

let bins = [as_bin!("name", "Carol"), as_bin!("age", 25_i64)];

let mut write_policy = WritePolicy::default();

write_policy.record_exists_action = RecordExistsAction::CreateOnly;

match client.put(&write_policy, &key, &bins).await {

    Ok(()) => println!("Record created"),

    Err(e @ Error::ServerError(ResultCode::KeyExistsError, ..)) => {

        // Record already present, propagate the Aerospike error.

        return Err(e.into());

    }

    Err(e) => return Err(e.into()),

}
```

When `main` returns `Result<(), Box<dyn std::error::Error>>`, propagate with `return Err(e.into())` or `?`. Do not use `Err("message".into())` (a string does not convert to `Box<dyn Error>`) or `return Err(Box::new(err))` (that yields `Box<aerospike::Error>`, not `Box<dyn Error>`). See [Error handling](https://aerospike.com/docs/develop/client/rust/2.1.0/error-handling).

> 📖 **API reference**: [`RecordExistsAction::CreateOnly`](https://docs.rs/aerospike/latest/aerospike/policy/enum.RecordExistsAction.html) | [`Client::put`](https://docs.rs/aerospike/latest/aerospike/struct.Client.html#method.put) | [`ResultCode::KeyExistsError`](https://docs.rs/aerospike/latest/aerospike/enum.ResultCode.html)

See also the [connect example](https://aerospike.com/docs/develop/client/rust/2.1.0/connect) for another create-only walkthrough.

## Writing record with time-to-live (TTL)

The following example specifies a time-to-live value for a record on a write, setting the record expiration to two seconds:

```rust
let mut policy = WritePolicy::default();

policy.expiration = Expiration::Seconds(2);

match client.put(&policy, &key, &bins).await {

    Ok(()) => println!("All records written"),

    Err(err) => println!("Error writing records: {}", err),

};
```

> 📖 **API reference**: [`WritePolicy::default`](https://docs.rs/aerospike/latest/aerospike/struct.WritePolicy.html) | [`Expiration`](https://docs.rs/aerospike/latest/aerospike/enum.Expiration.html) | [`Client::put`](https://docs.rs/aerospike/latest/aerospike/struct.Client.html#method.put)

Setting `policy.expiration` to certain specific values may have desirable side-effects, depending on your application.

| policy.expiration value | effect |
| --- | --- |
| `Expiration::NamespaceDefault` | applies the default TTL on the server side each time a record updates. |
| `Expiration::Never` | specifies that the record never expires. |
| `Expiration::DontUpdate` | requests the server to update a record without changing it’s TTL. (Requires Aerospike Server version 3.10.1 or later.) |

If left unchanged from creation, the `policy.expiration` value defaults to `Expiration::NamespaceDefault`.

::: note
Setting per-record TTLs with `Expiration::Seconds` requires Aerospike Enterprise Edition. Community Edition returns a `FailForbidden` error. To run the examples on CE, omit the `expiration` setting or use `Expiration::NamespaceDefault`.
:::

## Read-modify-write

Record Read-Modify-Write (or Check-and-Set) involves:

-   Reading the record, and remembering its generation value for later.
-   Modifying the record at the application level.
-   Setting `policy.generation_policy` to `GenerationPolicy::ExpectGenEqual` while setting `policy.generation` to the previously set generation value.

This implements a form of optimistic locking; should the write operation fail, check for a generation error; if this error occurred, you know that the record was updated by another thread. This gives you the opportunity to retry the operation in the event of a collision. This is similar to [load-linked/store-conditional](https://en.wikipedia.org/wiki/Load-link/store-conditional) optimistic mutual exclusion.

## Complete example

The following complete program connects to Aerospike on `localhost:3000`, writes two records with `name` and `age` bins, applies a TTL of 10 seconds, and then closes the client connection.

```rust
use aerospike::{as_bin, as_key, Client, ClientPolicy, Error, Expiration, WritePolicy};

#[tokio::main]

async fn main() -> Result<(), Error> {

    // Connect to Aerospike running on localhost.

    let client_policy = ClientPolicy::default();

    let client = Client::new(&client_policy, &"127.0.0.1:3000").await?;

    // Create records with bins name + age and a TTL of 10 seconds.

    let mut write_policy = WritePolicy::default();

    write_policy.expiration = Expiration::Seconds(10);

    let records = [("user-1", "Alice", 29), ("user-2", "Bob", 34)];

    for (user_key, name, age) in records {

        let key = as_key!("test", "users", user_key);

        let bins = [as_bin!("name", name), as_bin!("age", age)];

        client.put(&write_policy, &key, &bins).await?;

        println!("Created record: key={user_key}, name={name}, age={age}, ttl=10s");

    }

    // Close the connection.

    client.close().await?;

    println!("Connection closed");

    Ok(())

}
```

> 📖 **API reference**: [`Client::new`](https://docs.rs/aerospike/latest/aerospike/struct.Client.html#method.new) | [`WritePolicy::default`](https://docs.rs/aerospike/latest/aerospike/struct.WritePolicy.html#method.default) | [`Expiration::Seconds`](https://docs.rs/aerospike/latest/aerospike/enum.Expiration.html#variant.Seconds) | [`as_key!`](https://docs.rs/aerospike/latest/aerospike/macro.as_key.html) | [`as_bin!`](https://docs.rs/aerospike/latest/aerospike/macro.as_bin.html) | [`Client::put`](https://docs.rs/aerospike/latest/aerospike/struct.Client.html#method.put) | [`Client::close`](https://docs.rs/aerospike/latest/aerospike/struct.Client.html#method.close)

### Expected results

When the Aerospike server is running and reachable on `127.0.0.1:3000`, output is similar to:

```text
Created record: key=user-1, name=Alice, age=29, ttl=10s

Created record: key=user-2, name=Bob, age=34, ttl=10s

Connection closed
```