dynamodb-comfort v1.0.1
Common Helper Classes for Dealing With AWS DynamoDB
When working with AWS' DynamoDB on a node.js stack you have to deal with the JavaScript SDK DocumentClient. Using this SDK can be sometimes - let me say - unhandy in many real use cases. To make my life easier and to make my development work more smooth I have written some helper classes to deal with DynamoDB with the hope, that Amazon will add some of the features in their SDK in the future.
I would highly appreciate feedback to root@knatec.at and will add further helper functionalities to this repository.
Updating Entries in a DynamoDB Table
When you are updating entries in the DynamoDB table, you basically need to do something like this:
const params = {
TableName: "vehicles",
Key: { userId: "1db01af9-70d8-48cd-90dd-f61aaba88363", createdTimestamp: 1580334958592 },
UpdateExpression: "set #name = :name, #mileage = :mileage, #note = :note, #licensePlate = :licensePlate",
ExpressionAttributeNames: {
"#licensePlate": "licensePlate",
"#name": "name",
"#mileage": "mileage",
"#note": "note"
},
ExpressionAttributeValues: {
":licensePlate": dto.licensePlate,
":mileage": dto.mileage,
":name": dto.name,
":note": dto.note
},
ReturnValues: "ALL_NEW"
}
const result = await this.documentClient.update(params).promise();
return result.Attributes;
We are focusing on the properties ExpressionAttributeNames, ExpressionAttributeValues and UpdateExpression. It is very unhandy to always define those three types of properties. If you want to remove one property from updating, you have to remove it on three places - same for adding a new property.
The ExpressionAttributeNames has not necessarily be set, but I got used to it as you may run into annoying bugs if you are using reserved keys like "status".
So the following code will result in an error:
const params = {
TableName: process.env.vehiclesTableName,
Key: { userId: "1db01af9-70d8-48cd-90dd-f61aaba88363", createdTimestamp: 1580334958592 },
UpdateExpression: "set status = :status, name = :name, mileage = :mileage, note = :note, licensePlate = :licensePlate",
ExpressionAttributeValues: {
":licensePlate": "XX XXX XX",
":mileage": 2100,
":name": "Porsche GT 3",
":note": "Cool car",
":status": "SERVICE"
},
ReturnValues: "ALL_NEW"
}
const result = await this.documentClient.update(params).promise();
=> UpdateExpression: Attribute name is a reserved keyword; reserved keyword: status
Setting an empty string for a field will also result in an error message if you have not created the DocumentClient with the "convertEmptyValues: true" option => ExpressionAttributeValues contains invalid value: One or more parameter values were invalid: An AttributeValue may not contain an empty string for key :note
In my opinion the "convertEmptyValues: true" should be the standard behavior for dealing with empty values: also see Discussion on GitHub, that is why there is a dynamodb.factory.js file in that project that is configuring the documentClient with that option.
In order to focus on WHAT attributes you want to store to the DynamoDB instead of HOW you have to declare this params object you get the UpdateExpressionBuilder class.
Usage of UpdateExpressionBuilder
The UpdateExpressionBuilder is handling reserved field names, filters undefined attributes and builds the 3 needed information mentioned above (UpdateExpression, ExpressionAttributeValues, ExpressionAttributeNames) by just defining what properties need to be updated.
Example usage:
const { UpdateExpressionBuilder } = require("dynamodb-comfort");
...
const builder = new UpdateExpressionBuilder(dto);
const updateExpression = builder
.withProperty("name")
.withProperty("mileage")
.withProperty("note")
.withProperty("licensePlate")
.build();
const params = {
TableName: process.env.vehiclesTableName,
Key: { userId: "1db01af9-70d8-48cd-90dd-f61aaba88363", createdTimestamp: 1580334958592 },
ReturnValues: "ALL_NEW"
}
const result = await this.documentClient.update(Object.assign(params, updateExpression)).promise();
return result.Attributes;
Yes, it is that easy. No more worrying about undefined attributes, no worrying about reserved keywords and no worrying about creating the entire object for the update operation. Just define what attributes you want to update.