Drop DynamoDB Column with TypeScript

main

It's a NoSQL database that only needs a schema for the item's key. What we call "drop a column" in a relational database would be "remove an attribute from every item" in DynamoDB.

I have a productivity app that has a user table with 9k items. It has a few outdated attributes. Let's make a migration that will remove them!

import { tableName } from "../src/shared/db/tableName"
import { documentClient } from "../src/shared/db"
import { User } from "../src/users/User"
import { Key } from "aws-sdk/clients/dynamodb"

type Item = Pick<User, "id">

const removeAttribute = async (attributeName: string) => {
  const recursiveProcess = async (lastEvaluatedKey?: Key) => {
    const { Items, LastEvaluatedKey } = await documentClient
      .scan({
        TableName: tableName.users,
        ExclusiveStartKey: lastEvaluatedKey,
        ExpressionAttributeNames: {
          "#attributeName": attributeName,
          "#id": "id",
        },
        FilterExpression: "attribute_exists(#attributeName)",
        ProjectionExpression: "#id",
      })
      .promise()

    await Promise.all(
      (Items as Item[]).map(async ({ id }) => {
        await documentClient
          .update({
            TableName: tableName.users,
            Key: {
              id,
            },
            AttributeUpdates: {
              [attributeName]: {
                Action: "DELETE",
              },
            },
          })
          .promise()

        console.log(`✅ Remove ${attributeName} from ${id}`)
      })
    )

    if (LastEvaluatedKey) {
      await recursiveProcess(LastEvaluatedKey)
    }
  }

  recursiveProcess()
}

removeAttribute("goals")

Here we have a function that receives attribute name and removes it from every item. It's a recursive function because the scan returns a paginated result. First, we get items that have the attribute. Then we go over every item and call the update method with a delete action for the attribute. If there are still items to update, we go with the recursion.