How to check the existing items and prevent unintended overwrites when adding a record in DynamoDB

In this article, we're going to discuss how to check for duplicates when inserting a record in dynamodb.

Let's consider a simple dynamodb table user-jobs, where userId is the primary key and you've another attribute jobId where you store the job id.

You want to prevent multiple jobs from running for the same user. For example, if the user user1 already has a job job1 running - the system should prevent him to create another job.

You can use ConditionExpression in DynamoDB for achieving the same.

Before discussing ConditionExpression , let us discuss what would happen if we don't use condition expression.

Below is a simple typescript to create a record in a DynamoDB table

export async function addJob(job: IJob) {
  const params: PutItemCommandInput = {
    TableName: TABLE_NAME,
    Item: marshall(job),
  };
  const command = new PutItemCommand(params);
  const result = await ddbClient.send(command);
  console.log("result in addJob:", result);
}

If you execute the below code(which adds 2 jobs - one after the other), the second statement with job2 as jobId will overwrite the first record in dynamodb.

await addJob({
    userId: "user1",
    jobId: "job1",
  });
  await addJob({
    userId: "user1",
    jobId: "job2",
  });

However, you don't want the second record to be inserted as the user already has a job ( job1 ) running for him.

In this case, you can use ConditionExpression , as shown below. The only change to the previous code snippet is that we've added ConditionExpression property in PutItemCommand input parameters.

We're using attribute_not_exists condition expression to check whether there is an existing record with the same attribute(userId in our case). If it exists, the operation will fail and the second record will not be inserted.


export async function addJob(job: IJob) {
  const params: PutItemCommandInput = {
    TableName: TABLE_NAME,
    Item: marshall(job),
    ConditionExpression: "attribute_not_exists(userId)",
  };
  const command = new PutItemCommand(params);
  const result = await ddbClient.send(command);
  console.log("result in addJob:", result);
}

Now, we're going to execute the same set of statements that we used earlier - copied again below for reference

await addJob({
    userId: "user1",
    jobId: "job1",
  });
  await addJob({
    userId: "user1",
    jobId: "job2",
  });

The first record will be inserted as there is no job for the user user1 and the second statement will fail with 400 error as shown below

ConditionExpression error
ConditionExpression error

attribute_not_exists is one of the condition expressions available in dynamodb and below are the different types of condition express that you can use in DynamoDB

  • attribute_exists() - Check whether an attribute exists
  • attribute_not_exists() - Opposite behavior to previous condition expression
  • attribute_type() - Check whether an attribute is of a specific type
  • begins_with() - Check whether the attribute value begins with a particular substring
  • contains() - Check whether a string contains a particular substring
  • size() - Check the size of an attribute value. For example, for string attribute - you can use it to check the length of the string.

Please let me know your thoughts in the comments.