Skip to content

Updates

Dynorm provides a fluent Update Builder for atomic DynamoDB update operations.

Basic Updates

Update by ID

// Create update builder
updates := dynorm.Update().
    Set("Status", "verified").
    Set("VerifiedAt", time.Now())

// Apply to entity by ID
user, err := UserRepo.Update("01HQ3K...", updates)

Update via Query

// Update all matching entities
count, err := UserRepo.
    Where("Status", "=", "pending").
    Update(dynorm.Update().Set("Status", "active"))

fmt.Printf("Updated %d users\n", count)

Update Operations

Set

Set a field to a value:

updates := dynorm.Update().
    Set("Status", "active").
    Set("Email", "new@example.com").
    Set("UpdatedAt", time.Now())

SetIfNotExists

Set a field only if it doesn't exist:

updates := dynorm.Update().
    SetIfNotExists("DefaultRole", "user")

Remove

Remove attributes from the item:

updates := dynorm.Update().
    Remove("TempField", "OldField")

Increment / Decrement

Atomic numeric operations:

// Increment
updates := dynorm.Update().
    Increment("ViewCount", 1).
    Increment("Score", 10)

// Decrement
updates := dynorm.Update().
    Decrement("Stock", 1).
    Decrement("Balance", 100)

Add

Add values to a number or set:

// Add to number
updates := dynorm.Update().
    Add("Points", 50)

// Add to set
updates := dynorm.Update().
    Add("Tags", []string{"featured", "new"})

Delete (Set Elements)

Remove elements from a set:

updates := dynorm.Update().
    Delete("Tags", "old-tag", "deprecated")

Append / Prepend (Lists)

Add elements to lists:

// Append to end
updates := dynorm.Update().
    Append("Comments", comment1, comment2)

// Prepend to beginning
updates := dynorm.Update().
    Prepend("RecentViews", viewRecord)

Conditional Updates

Add conditions that must be met for the update to succeed:

updates := dynorm.Update().
    Set("Status", "published").
    Where("Status", "=", "draft")

user, err := UserRepo.Update(id, updates)
if err != nil {
    // Condition not met or other error
}

Available Conditions

// Field equals value
updates.Where("Status", "=", "draft")

// Field not equals
updates.Where("Status", "!=", "banned")

// Comparisons
updates.Where("Version", ">=", 1)
updates.Where("Age", "<", 18)

// Attribute exists
updates.WhereExists("Email")

// Attribute doesn't exist
updates.WhereNotExists("DeletedAt")

Multiple Conditions

updates := dynorm.Update().
    Set("Status", "active").
    Where("Status", "=", "pending").
    Where("Age", ">=", 18).
    WhereExists("Email")

Optimistic Locking

Use version fields for concurrent update safety:

type User struct {
    dynorm.Entity
    Version int `dynorm:"version"`
    // ...
}

// Update with optimistic locking
updates := dynorm.Update().
    Set("Status", "active").
    WithOptimisticLock(currentVersion)
// Automatically checks version and increments it

user, err := UserRepo.Update(id, updates)
if err != nil {
    // Version mismatch - someone else updated the entity
}

Manual Version Control

updates := dynorm.Update().
    Set("Status", "active").
    IncrementVersion() // Increment version field

// Or with explicit check
updates := dynorm.Update().
    Set("Status", "active").
    Where("Version", "=", 5).
    Increment("Version", 1)

Chaining Operations

Combine multiple operations:

updates := dynorm.Update().
    Set("Status", "active").
    Set("ActivatedAt", time.Now()).
    Increment("LoginCount", 1).
    Remove("TempToken").
    Append("ActivityLog", "Account activated").
    Where("Status", "=", "pending")

user, err := UserRepo.Update(id, updates)

Examples

Activate User Account

func ActivateUser(userID string) (*User, error) {
    updates := dynorm.Update().
        Set("Status", "active").
        Set("ActivatedAt", time.Now()).
        Remove("ActivationToken").
        Where("Status", "=", "pending")

    return UserRepo.Update(userID, updates)
}

Increment Counter

func IncrementViewCount(postID string) (*Post, error) {
    updates := dynorm.Update().
        Increment("ViewCount", 1).
        Set("LastViewedAt", time.Now())

    return PostRepo.Update(postID, updates)
}

Add Tag to Post

func AddTag(postID string, tag string) (*Post, error) {
    updates := dynorm.Update().
        Append("Tags", tag)

    return PostRepo.Update(postID, updates)
}

Safe Stock Decrement

func DecrementStock(productID string, quantity int) (*Product, error) {
    updates := dynorm.Update().
        Decrement("Stock", int64(quantity)).
        Where("Stock", ">=", quantity) // Only if enough stock

    return ProductRepo.Update(productID, updates)
}

Bulk Status Update

func DeactivateOldUsers(daysSinceLogin int) (int, error) {
    cutoff := time.Now().AddDate(0, 0, -daysSinceLogin)

    return UserRepo.
        Where("LastLoginAt", "<", cutoff).
        Where("Status", "=", "active").
        Update(dynorm.Update().
            Set("Status", "inactive").
            Set("DeactivatedAt", time.Now()))
}

Error Handling

updates := dynorm.Update().
    Set("Status", "active").
    Where("Status", "=", "pending")

user, err := UserRepo.Update(id, updates)
if err != nil {
    // Could be:
    // - ConditionalCheckFailedException (condition not met)
    // - ValidationException (invalid update)
    // - ResourceNotFoundException (entity not found)
    return err
}

Best Practices

Do

  • Use atomic operations for counters
  • Use optimistic locking for concurrent updates
  • Use conditions to ensure safe updates
  • Combine related updates in single operation

Don't

  • Read-modify-write when atomic update works
  • Ignore conditional check failures
  • Update without version checks in concurrent scenarios

Update Flow

sequenceDiagram
    participant App
    participant Dynorm
    participant DynamoDB

    App->>Dynorm: Update(id, updates)
    Dynorm->>Dynorm: Build UpdateExpression
    Dynorm->>Dynorm: Build ConditionExpression

    alt Has Conditions
        Dynorm->>DynamoDB: UpdateItem + Condition
        alt Condition Met
            DynamoDB-->>Dynorm: Updated Item
            Dynorm-->>App: Updated Entity
        else Condition Failed
            DynamoDB-->>Dynorm: ConditionalCheckFailed
            Dynorm-->>App: Error
        end
    else No Conditions
        Dynorm->>DynamoDB: UpdateItem
        DynamoDB-->>Dynorm: Updated Item
        Dynorm-->>App: Updated Entity
    end

Next Steps