How to Get Item by ID From Dynamodb Table with TypeScript?

June 21, 2022

2 min read

How to Get Item by ID From Dynamodb Table with TypeScript?
Watch on YouTube

My app has a table with users, and I usually query just a handful of fields instead of the whole user object. That's why we have a second parameter called attributes. It is an array of user keys.

export async function getUserById<T extends (keyof User)[]>(
  id: string,
  attributes?: T
): Promise<Pick<User, T[number]> | undefined> {
  const { Item } = await documentClient
    .get(mergeParams(getUserItemParams(id), projectionExpression(attributes)))
    .promise()

  return Item as Pick<User, T[number]>
}

To simplify interactions with DynamoDB, I have a handful of helpers.

projectionExpression converts an array to an object we can pass to the document client.

export const projectionExpression = (attributes?: string[]) => {
  if (!attributes) {
    return {}
  }

  const ProjectionExpression = attributes
    .map((attr) => `${attr.includes(".") ? "" : "#"}${attr}, `)
    .reduce((acc, str) => acc + str)
    .slice(0, -2)

  const attributesToExpression = attributes.filter(
    (attr) => !attr.includes(".")
  )

  const ExpressionAttributeNames = attributesToExpression.reduce<{
    [key: string]: string
  }>((acc, attr) => {
    acc["#" + attr] = attr
    return acc
  }, {})

  return attributesToExpression.length
    ? { ProjectionExpression, ExpressionAttributeNames }
    : { ProjectionExpression }
}

I have a function like getUserItemParams for every table. It returns an object we need to get, update or delete an item.

export const getUserItemParams = (id: string) => ({
  TableName: tableName.users,
  Key: { id },
})

Sometimes those helpers can return an object with a shared field ExpressionAttributeNames. To resolve such clashes, I use the mergeParams function.

export const mergeParams = (...params: any[]) =>
  params.reduce(
    (acc, param) =>
      Object.entries(param).reduce((acc, [key, value]) => {
        acc[key] = Array.isArray(value)
          ? [...acc[key], ...value]
          : typeof value === "object"
          ? { ...acc[key], ...value }
          : value
        return acc
      }, acc),
    {}
  )