Skip to content

Put (Create/Replace)

The Put API is how you change the data in your Store. It handles both creating new Items and updating existing Items, with the simple behavior of replacing anything at the same key path. For these examples we’ll use the schema defined in Example: Movies Schema.

Creating New Items

The Put API adds new Items to your Store. You can either provide an Item that has all of its ID fields populated, or if you have an initialValue field you can leave it unpopulated to allow StatelyDB to choose the ID. In this example, we add a new Movie, and we don’t populate its id field because that field is defined with an initialValue of uuid, so StatelyDB will assign a new UUID to it.

15 collapsed lines
package main
import (
"context"
"fmt"
"os"
"slices"
"strconv"
"time"
"github.com/StatelyCloud/go-sdk/stately"
// This is the code you generated from schema
"github.com/StatelyCloud/stately/go-sdk-sample/schema"
)
func samplePut(
ctx context.Context,
client stately.Client,
) ([]byte, error) {
movie := &schema.Movie{
// Id: []byte(...) will be auto-generated by put
// because of its uuid initialValue
Title: "Starship Troopers 2",
Genre: "Sci-Fi",
Year: 1997,
Duration: int64(
(2*time.Hour + 9*time.Minute).Seconds(),
),
Rating: "R",
}
item, err := client.Put(ctx, movie)
if err != nil {
return nil, err
}
movie = item.(*schema.Movie)
return movie.Id, nil
}

Replacing Items

Put can also be used to replace existing Items. We say “replace” instead of “update” because the new Item completely replaces the old Item. To replace an existing Item, you Put an Item that has the same ID fields as an existing Item, and that will overwrite the original Item.

15 collapsed lines
package main
import (
"context"
"fmt"
"os"
"slices"
"strconv"
"time"
"github.com/StatelyCloud/go-sdk/stately"
// This is the code you generated from schema
"github.com/StatelyCloud/stately/go-sdk-sample/schema"
)
func sampleUpdate(
ctx context.Context,
client stately.Client,
movieID []byte,
) error {
// Replace the Movie at movieID with this new item.
_, err := client.Put(ctx, &schema.Movie{
Id: movieID,
Title: "Starship Troopers",
Year: 1997,
Genre: "Sci-Fi",
Duration: int64(
(2*time.Hour + 9*time.Minute).Seconds(),
),
Rating: "R",
})
if err != nil {
return err
}
return nil
}

Batch Put

If you have multiple Items, it can be more efficient to put them all at once using PutBatch. This allows up to 50 Items, and the puts are applied atomically, meaning either all puts will succeed, or none of them will. You can combine new Items and updates to existing Items in the same batch - Items that have an initialValue will always be newly created, while existing Items will be replaced.

15 collapsed lines
package main
import (
"context"
"fmt"
"os"
"slices"
"strconv"
"time"
"github.com/StatelyCloud/go-sdk/stately"
// This is the code you generated from schema
"github.com/StatelyCloud/stately/go-sdk-sample/schema"
)
func sampleBatchPut(
ctx context.Context,
client stately.Client,
) error {
// Put some thriller movies
_, err := client.PutBatch(ctx,
&schema.Movie{
Title: "Seven",
Rating: "R",
Year: 1995,
Genre: "Thriller",
Duration: int64(
(2*time.Hour + 7*time.Minute).Seconds(),
),
},
&schema.Movie{
Title: "Heat",
Rating: "R",
Year: 1995,
Genre: "Thriller",
Duration: int64(
(2*time.Hour + 50*time.Minute).Seconds(),
),
},
)
return err
}

Unique Constraints

When StatelyDB chooses the ID for your initialValue field, the Item is guaranteed to be new - it won’t overwrite any existing Item. You can use this to your advantage to implement “unique constraints”—if a value is generated for an initialValue field and that field is used in one of the Item’s key paths, StatelyDB also ensures that an Item doesn’t already exist under any of the Item’s other key paths. For example, imagine you have a User Item type that defines two key paths, /user-:id and /email-:email, and the id field has initialValue: "uuid". When you Put a User without specifying its ID, StatelyDB will generate a new UUID for the ID, and will fail the Put if another User already exists with the same email.

Partial and Conditional Updates

StatelyDB does not currently have an API for partial updates (changing only some fields of an Item). For now, those operations are best handled using a transaction. Within a transaction, you can Get the existing state of an Item, make changes to it (or return early if your condition is not met), and then Put the Item back to the Store. The transaction guarantees that the Item didn’t change during that sequence of operations. This is very flexible since you can implement any logic you want within a transaction, but we do intend to introduce convenience APIs for partial updates and some common conditions in the future.

Must Not Exist

However, a common pattern is to create an item only if that item (or another item with the same ID) doesn’t exist. You can use the Put API with per-Item options to specify a “Must Not Exist” constraint on the Put. The Put will fail if the item already exists at one or more of its key paths. This works for both singular and batch updates, and you can mix and match items with a “Must Not Exist” constraint with ones that don’t in a single batch.

15 collapsed lines
package main
import (
"context"
"fmt"
"os"
"slices"
"strconv"
"time"
"github.com/StatelyCloud/go-sdk/stately"
// This is the code you generated from schema
"github.com/StatelyCloud/stately/go-sdk-sample/schema"
)
func samplePutMustNotExist(
ctx context.Context,
client stately.Client,
movie *schema.Movie,
) (*schema.Movie, error) {
// This will fail if the movie already exists
item, err := client.Put(ctx, stately.WithPutOptions{
Item: movie,
MustNotExist: true,
})
if err != nil {
return nil, err
}
return item.(*schema.Movie), nil
}