Front-End Web & Mobile

Amazon DynamoDB on Mobile – Part 3: Writing Data

Version 2 of the AWS Mobile SDK

  • This article and sample apply to Version 1 of the AWS Mobile SDK. If you are building new apps, we recommend you use Version 2. For details, please visit the AWS Mobile SDK page.
  • This content is being maintained for historical reference.

In our previous posts (Part 1, Part 2), we discussed how we can read data from an Amazon DynamoDB table using the AWS Mobile SDKs. In this post, we will discuss our options for writing data to DynamoDB.

Creating/Updating a Record

If we want to write a record in our table, we can use the PutItem API action and supply the full item we wish to write. Other than respecting the schema for our key (number for UserId, string for RecordId), we can use any value type we so choose for any other attribute in the item, mixing and matching types however we choose.

iOS

// create the request, specify the table
DynamoDBPutItemRequest *putItemRequest = [DynamoDBPutItemRequest new];
request.tableName = @"UserTableExample";

// Each attribute will be a DynamoDBAttributeValue
DynamoDBAttributeValue *value = [[DynamoDBAttributeValue alloc] initWithN:@"1234"];
[request.item setValue:value forKey:@"UserId"];

// The item is an NSMutableDictionary, keyed by the attribute name
value = [[DynamoDBAttributeValue alloc] initWithS:@"FavoriteColors"];
[request.item setValue:value forKey:@"RecordId"];

// Now that we've added our required key attributes,
// we can add anything else we choose, even a set of strings
value = [DynamoDBAttributeValue new];
[value addSS:@"Green"];
[value addSS:@"Red"];
[value addSS:@"Black"];
[request.item setValue:value forKey:@"Data"];

// process the request
DynamoDBPutItemResponse *response = [self.ddb putItem:request];

Android

// Each attribute will be an AttributeValue
Map<String,AttributeValue> item = new HashMap<String, AttributeValue>();
item.put("UserId", 
         new AttributeValue().withN("1234"));
item.put("RecordId", 
         new AttributeValue().withS("FavoriteColors"));
	    
// Now that we've added our required key attributes,
// we can add anything else we choose, even a set of strings
item.put("Data",
         new AttributeValue().withSS("Green", "Red", "Black"));
	    
// Construct our request, supply table name
PutItemRequest putItemRequest = new PutItemRequest("UserTableExample", item);
	    
// process the request
PutItemResult putItemResult = ddbClient.putItem(putItemRequest);

As we saw with our read requests in Part 2, we can optionally receive information about our consumed capacity units for our PutItem calls.

iOS

// Ask for our total consumed capacity
putItemRequest.returnConsumedCapacity = @"TOTAL";  

// Execute the request
DynamoDBPutItemResponse *putItemResponse = [self.ddb putItem:putItemRequest];

// Log our consumed write capacity (implied casting down to int)
NSLog(@"Consumed write capacity for putItem: %d", [putItemResponse.consumedCapacity.capacityUnits integerValue]);

Android

// Ask for our total consumed capacity
putItemRequest.setReturnConsumedCapacity(ReturnConsumedCapacity.TOTAL);

// Execute the request
PuttItemResult result = ddbClient.putItem(putItemRequest);

// Log our consumed capacity (casting to int for whole number)
Log.i(LOG_TAG, "Consumed write capacity for putItem: = " + result.getConsumedCapacity().getCapacityUnits().intValue());

PutItem can be used for both inserting new data and updating existing data. DynamoDB also supports an UpdateItem where only modified records need to be supplied, but as our schema is rather small, it is not applicable to our use case.

Conditional Writes

PutItem will insert a new item or overwrite an existing item if it exists. If we want to ensure that our record is new, we can optionally pass an ExpectedAttributeValue to ensure the given key value doesn’t exist.

iOS

DynamoDBExpectedAttributeValue *checkExists = [DynamoDBExpectedAttributeValue new];
checkExists.exists = NO;
[putItemRequest.expected setValue:checkExists forKey:@"RecordId"];

Android

Map<String,ExpectedAttributeValue> expected = new HashMap<String,ExpectedAttributeValue>();
expected.put("RecordId", new ExpectedAttributeValue().withExists(false));	    
putItemRequest.setExpected(expected);

Additionally, if we want to ensure that we only update a given record if it was in a known state beforehand, we can specify that expected state, also via an ExpectedAttributeValue. This could be used to implement an atomic counter, perhaps for a number of plays in our game.

iOS

// construct our request
DynamoDBPutItemRequest *putItemRequest = [DynamoDBPutItemRequest new];
request.tableName = @"UserTableExample";
DynamoDBAttributeValue *value = [[DynamoDBAttributeValue alloc] initWithN:@"1234"];
[putItemRequest.item setValue:value forKey:@"UserId"];
value = [[DynamoDBAttributeValue alloc] initWithS:@"Plays"];
[putItemRequest.item setValue:value forKey:@"RecordId"];
value = [[DynamoDBAttributeValue alloc] initWithN:@"101"];
[putItemRequest.item setValue:value forKey:@"Data"];

// Our expected attribute value also uses a DynamoDBAttributeValue
// We expect it to be 100 (one less than the value we're incrementing to)
value = [[DynamoDBAttributeValue alloc] initWithN:@"100"];
DynamoDBExpectedAttributeValue *attr = [[DynamoDBExpectedAttributeValue alloc] initWithValue:value];

// Add the expected value to our dictionary of expected values
[putItemRequest.expected setValue:checkExists forKey:@"Data"];

// process the request
DynamoDBPutItemResponse *putItemResponse = [self.ddb putItem:putItemRequest];

Android

// construct our request
Map<String,AttributeValue> item = new HashMap<String, AttributeValue>();
item.put("UserId", 
         new AttributeValue().withN("1234"));
item.put("RecordId", 
         new AttributeValue().withS("Plays"));
item.put("Data", 
         new AttributeValue().withN("101"));
PutItemRequest putItemRequest = new PutItemRequest("UserTableExample", item);
        
// Our expected attribute value also uses a AttributeValue
// We expect it to be 100 (one less than the value we're incrementing to)
AttributeValue counter = new AttributeValue().withN("100");
Map<String,ExpectedAttributeValue> expected = new HashMap<String,ExpectedAttributeValue>();
expected.put("Data", new ExpectedAttributeValue().withValue(counter));
        
putItemRequest.setExpected(expected);
	    
// process the request
PutItemResult putItemResult = ddbClient.putItem(putItemRequest);

If the conditional write fails, an exception will be thrown on Android, and an NSError or NSException will be generated on iOS (see our previous post on how to disable exceptions in the iOS SDK). Our code should be updated to handle the failure and retry the update.

iOS

// process the request
DynamoDBPutItemResponse *putItemResponse = [self.ddb putItem:putItemRequest];

// Exceptions have been disabled, so our response may contain an error
if (nil != putItemReponse.error) {
    NSString *errorCode = [response.error.userInfo objectForKey:@"errorCode"];
            
    if ([errorCode isEqualToString:@"ConditionalCheckFailedException"]) {
        // our conditional update failed, fetch item from DynamoDB 
        // to get current value and update request to retry
        ...
    }
    else {
        // some other class of error occurred
        ...
    }
}

Android

try {
    // process the request
    PutItemResult putItemResult = ddbClient.putItem(putItemRequest);
}
catch (ConditionalCheckFailedException error) {
    // our conditional update failed, fetch item from DynamoDB 
    // to get current value and update request to retry
    ...
}
catch (AmazonServiceException error) {
    // some other class of error occurred
    ...
}

For more information about conditional writes, please refer to the Amazon DynamoDB Developer Guide.

Deleting an Item

If we want to delete a record from our table, we can use the DeleteItem API action, which only requires that we pass the key of the item we wish to delete.

iOS

// create our request
DynamoDBDeleteItemRequest *deleteItemRequest = [DynamoDBDeleteItemRequest new];
request.tableName = @"UserTableExample";

// Need to specify the key of our item, which is an NSDictionary of our primary key attribute(s)
DynamoDBAttributeValue *value = [[DynamoDBAttributeValue alloc] initWithN:@"1234"];
[deleteItemRequest.key setValue:value forKey:@"UserId"];
value = [[DynamoDBAttributeValue alloc] initWithS:@"FavoriteColors"];
[deleteItemRequest.key setValue:value forKey:@"RecordId"];

DynamoDBDeleteItemResponse *deleteItemResponse = [self.ddb deleteItem:deleteItemRequest];

Android

// Need to specify the key of our item, which is a Map of our primary key attribute(s)
Map<String,AttributeValue> key = new HashMap<String,AttributeValue>();
key.put("UserId", new AttributeValue().withN("1234"));
key.put("RecordId", new AttributeValue().withS("FavoriteColors"));
	    
// create our request
DeleteItemRequest deleteItemRequest = new DeleteItemRequest("UserTableExample", key);

// process our request
DeleteItemResult deleteItemResponse = ddbClient.deleteItem(deleteItemRequest);

We hope this illustrates the basics of writing and modifying data to DynamoDB with the AWS Mobile SDKs. Please stay tuned for the final post in this series where we discuss some of the latest enhancements to Amazon DynamoDB and how you can use them in your mobile apps.

The code samples included in this post are for the DynamoDB API version 2012-08-10 and will work only with the AWSDynamoDB.framework on iOS and the com.amazonaws.services.dynamodbv2 package on Android. See our previous post on how to convert your iOS application to use the new service level frameworks.

Further Reading