Skip to content

AWS | DynamoDB

DynamoDB Setup

Cost

On demand pricing
Cost for one table:
Total read capacity units = 5
Total write capacity units = 5
Estimated monthly cost = $2.91 / month | 29,10 SEK / month

Query vs Scan

Query is used to retrieve a subset of items from a table based on a primary key value or a secondary index value. Scan is used to retrieve all items from a table or a secondary index.

Query is faster than scan

Setup

Create table with primary key = id
Create item

Example table

Setup - Table name = user_images - Partition Key: userId (String) value = qsqsqsq-sqsqsq-qsqsqs-qsqsq - Sort Key: imagesCollectionId (String)

Attributes - imagesId (String) - url (String) - timestamp: (String)

Item for each user

{
 "userId": "awdawdawd-awdawdawd-awdawd-awdawdawd-awdawdawd",
 "imagesCollection": [
  {
   "images": [
    {
     "imageId": "awdawdawd-awdawdawd-awdawd-awdawdawd-awdawdawd",
     "url": "https://s3.amazonaws.com/awdawdawd-awdawdawd-awdawd-awdawdawd-awdawdawd"
    },
    {
     "imageId": "09ed4bf2-e43c-4a25-804b-5f55bc94090d",
     "url": "https://s3.amazonaws.com/awdawdawd-awdawdawd-awdawd-awdawdawd-awdawdawd"
    },
    {
     "imageId": "a49da393-db67-4886-951c-a9a63b66348b",
     "url": "https://s3.amazonaws.com/awdawdawd-awdawdawd-awdawd-awdawdawd-awdawdawd"
    },
    {
     "imageId": "f0116257-4c8d-4edb-b48e-cbe500a67d6a",
     "url": "https://s3.amazonaws.com/awdawdawd-awdawdawd-awdawd-awdawdawd-awdawdawd"
    }
   ],
   "imagesCollectionId": "awdawdawd-awdawdawd-awdawd-awdawdawd-awdawdawd",
   "timestamp": "2022-03-21T10:30:00Z"
  }
 ]
}

AWS-cli

Create Table

import {CreateTableInput} from "aws-sdk/clients/dynamodb";
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
  if(token){
    const params: CreateTableInput = {
      TableName: 'user-images-test',
      KeySchema: [
        { AttributeName: 'userId', KeyType: 'HASH' },
      ],
      AttributeDefinitions: [
        { AttributeName: 'userId', AttributeType: 'S', },
      ],
      ProvisionedThroughput: {
        ReadCapacityUnits: 1,
        WriteCapacityUnits: 1,
      },
    };

    try {
      const data = await dynamodb.createTable(params).promise();
      console.log(data);
      res.status(200).json({ message: 'Table created successfully' });
    } catch (err) {
      console.log(err);
      res.status(500).json({ message: 'Failed to create table' });
    }
  }

}
export default handler;

Put Item

interface ImageObject {
  S: string;
}
interface Image {
  M: {
    imageId: ImageObject;
    url: ImageObject;
  };
}
interface ImagesCollectionObject {
  S: string;
}
interface ImagesCollection {
  images: { L: Image[] };
  imagesCollectionId: ImagesCollectionObject;
  timestamp: ImageObject;
}
interface Images {
  L: [{ M: ImagesCollection }];
}
import {PutItemInput} from "aws-sdk/clients/dynamodb";
import {decode} from "jsonwebtoken";
export const images: Images = {
  "L": [{
    "M": {
      "images": {
        "L": [
          {"M": {"imageId": {"S": "cb9a75a2-2553-4c9f-ab0a-1388c54d6621"}, "url": {"S": "https://s3.amazonaws.com//cb9a75a2-2553-4c9f-ab0a-1388c54d6621"}}},
          {"M": {"imageId": {"S": "09ed4bf2-e43c-4a25-804b-5f55bc94090d"}, "url": {"S": "https://s3.amazonaws.com//09ed4bf2-e43c-4a25-804b-5f55bc94090d"}}},
          {"M": {"imageId": {"S": "a49da393-db67-4886-951c-a9a63b66348b"}, "url": {"S": "https://s3.amazonaws.com//imagebot/a49da393-db67-4886-951c-"}}},
          {"M": {"imageId": {"S": "f0116257-4c8d-4edb-b48e-cbe500a67d6a"}, "url": {"S": "https://s3.amazonaws.com//imagebot/f0116257-4c8d-4edb-b48e-"}}}
        ]
      },
      "imagesCollectionId": {"S": "5ec5d9a2-3104-4472-89ad-fc45bf4ade51"},
      "timestamp": {"S": "2022-03-21T10:30:00Z"}
    }
  }]
}

const handler = async (req: NextApiRequest, res: NextApiResponse) => {
  const access_token = req.headers.authorization
  const token = access_token?.replace('Bearer ', '')
  if (token) {
    const subId = decode(token)?.sub as string
    const params: UpdateItemInput = {
      TableName: "user-images-test",
      Key: {"userId": {"S": subId}},
      UpdateExpression: "SET #images = list_append(#images, :new_images)",
      ExpressionAttributeNames: { "#images": "imagesCollection" },
      EExpressionAttributeValues: {":new_images": images},
    }

    try {
      const data = await dynamodb.updateItem(params).promise();
      console.log(data);
      res.status(200).json({message: 'Update successfully'});
    } catch (err) {
      console.log(err);
      res.status(500).json({message: 'Failed to update table'});
    }
  }
}
export default handler;

Get Item

const handler = async (req: NextApiRequest, res: NextApiResponse) => {
  const access_token = req.headers.authorization
  const token = access_token?.replace('Bearer ', '')
  if (token) {
    const subId = decode(token)?.sub as string
    const params: GetItemInput = {
      TableName: "user_images",
      Key: {"userId": {"S": subId}}
    }
    try {
      const data = await updatedynamoDB.getItem(params).promise();
      const item = data.Item as unknown as UserImages;
      const imagesCollection = item.imagesCollection;
      res.status(200).json(imagesCollection);
    } catch (err) {
      console.log(err);
      res.status(500).json({message: 'Failed to get item'});
    }
  }
}
export default handler;

Get one object from Item

Only works if knowing indexPosition and Id in advance

public QueryResponse getItemFiltered(String userId, String imagesCollectionId, String imageIndex) {
    log.info("userId: {}, imagesCollectionId: {}", userId, imagesCollectionId);
    DynamoDbClient dynamoDbClient = createClient(awsCredentials.getAccessKey(), awsCredentials.getSecretKey());

     Map<String, AttributeValue> expressionAttributeValues = new HashMap<>();
    expressionAttributeValues.put(":userId", AttributeValue.builder().s(userId).build());
    expressionAttributeValues.put(":imagesCollectionId", AttributeValue.builder().s(imagesCollectionId).build());

     QueryRequest queryRequest = QueryRequest.builder()
            .tableName(TABLE_NAME)
            .keyConditionExpression("userId = :userId")
            .expressionAttributeValues(expressionAttributeValues)
            .filterExpression("contains(imagesCollection[" + imageIndex + "].imagesCollectionId, :imagesCollectionId)")
            .projectionExpression("imagesCollection[" + imageIndex + "]")
            .build();

     return dynamoDbClient.query(queryRequest);
}

 private static DynamoDbClient createClient(String accessKey, String secretKey) {
    var basicCredentials = AwsBasicCredentials.create(accessKey, secretKey);
    return DynamoDbClient.builder()
            .region(Region.US_EAST_1)
            .credentialsProvider(StaticCredentialsProvider.create(basicCredentials))
            .build();
}